StoreKit2が発表されたのと同じタイミングで、App Store Server Notifications v2が発表されました。
App Store Server Notifications | Apple Developer Documentation
この通知ではAppleから様々な情報を受け取ることができます。利用する際には通知が本当にAppleから来たものか確認するためにJWSの検証を行う必要があります。
ここではphpでどのように検証を行うかについて整理します。内容に間違いがありましたらコメントお願いします。
また、App Store Connectの設定をv1にしたままでもRequest a Test Notification APIにより通知を飛ばすとv2仕様で通知が飛ぶようです。まずはこれを実行することでテスト通知を送信しつつ検証をすすめるのも良いでしょう。
Request a Test Notification | Apple Developer Documentation
signedPayloadの中身をドットで区切り、それぞれbase64 decodeするとで先頭から順にheader, payload, signatureを得ます。
php-jwt/JWT.php at 018dfc4e1da92ad8a1b90adc4893f476a3b41cb8 · firebase/php-jwt
次に証明書チェーンの検証を行います。
まずAppleのルート証明書を用意します。ダウンロードして.cerを.pemに変換します。Appleのルート証明書が更新される可能性があるのでそこに注意する必要がありますが、このコードのように毎回ダウンロードする必要は無いはずなのであくまでサンプルです。
php7.4以上ではopenssl_x509_verify()が使えるため、以下のように検証できます。
PHP: openssl_x509_verify - Manual
Validate StoreKit2 in-app purchase… | Apple Developer Forums
phpでSafetyNet APIのレスポンスの証明書を検証する | GRIPHONE ENGINEER'S BLOG
App Store Server Notifications | Apple Developer Documentation
この通知ではAppleから様々な情報を受け取ることができます。利用する際には通知が本当にAppleから来たものか確認するためにJWSの検証を行う必要があります。
ここではphpでどのように検証を行うかについて整理します。内容に間違いがありましたらコメントお願いします。
テスト通知送信
通知の内容をv1にするかv2にするかはApp Store Connectで切り替えることが可能です。テスト課金を行って通知を飛ばす場合、あらかじめ切り替えておきましょう。また、App Store Connectの設定をv1にしたままでもRequest a Test Notification APIにより通知を飛ばすとv2仕様で通知が飛ぶようです。まずはこれを実行することでテスト通知を送信しつつ検証をすすめるのも良いでしょう。
Request a Test Notification | Apple Developer Documentation
手順
通知が送信されるとこのようなデータを受信できます。{ "signedPayload": "eyJh ..."}signedPayload | Apple Developer Documentation
signedPayloadの中身をドットで区切り、それぞれbase64 decodeするとで先頭から順にheader, payload, signatureを得ます。
$contents = '{"signedPayload":"eyJh ... "; $signedPayload = json_decode($contents, true)['signedPayload']; list($jws_header, $jws_body, $jws_signature) = explode(".", $signedPayload); $header = json_decode(base64_decode($jws_header), true); $payload = json_decode(base64_decode($jws_body), true); $signature = base64_decode($jws_signature);まずheaderから証明書を取り出し、その証明書を変換しておきます。
$chain_array = $header['x5c']; $pem_from_x5c_cert_list = array(); foreach ($chain_array as $key => $chain_array_item) { $pem_from_x5c_cert = ( "-----BEGIN CERTIFICATE-----\n" . chunk_split($chain_array_item, 64, "\n") . "-----END CERTIFICATE-----\n" ); $pem_from_x5c_cert_list[] = $pem_from_x5c_cert; //証明書の内容を確認する場合 $chain_cert = openssl_x509_parse(openssl_x509_read($pem_from_x5c_cert)); echo('証明書の内容 : ' . PHP_EOL); echo ('name : ' . $chain_cert['name'] . PHP_EOL); }データの中身はpayloadに入っています。 そのデータについてまずsignatureで検証します。ここではfirebase/php-jwtを使いました。
php-jwt/JWT.php at 018dfc4e1da92ad8a1b90adc4893f476a3b41cb8 · firebase/php-jwt
(あらかじめ) use \Firebase\JWT\JWT; (コード) $jwt_decode_result = JWT::decode($signedPayload, new \Firebase\JWT\Key($pem_from_x5c_cert_list[0], 'ES256')); var_dump($jwt_decode_result);
次に証明書チェーンの検証を行います。
まずAppleのルート証明書を用意します。ダウンロードして.cerを.pemに変換します。Appleのルート証明書が更新される可能性があるのでそこに注意する必要がありますが、このコードのように毎回ダウンロードする必要は無いはずなのであくまでサンプルです。
$apple_cert_url = 'https://www.apple.com/certificateauthority/AppleRootCA-G3.cer'; $apple_cert_content = file_get_contents($apple_cert_url); $apple_pem = '-----BEGIN CERTIFICATE-----'.PHP_EOL .chunk_split(base64_encode($apple_cert_content), 64, PHP_EOL) .'-----END CERTIFICATE-----'.PHP_EOL;得られたAppleのルート証明書を含め、これらの証明書を順番に検証していきます。 まずappleの証明書を追加しておきます。
$pem_from_x5c_cert_list[] = $apple_pem;
php7.4以上ではopenssl_x509_verify()が使えるため、以下のように検証できます。
PHP: openssl_x509_verify - Manual
//検証 foreach ($chain_array as $key => $chain_array_item) { $result = openssl_x509_verify($pem_from_x5c_cert_list[$key + 1], openssl_get_publickey($pem_from_x5c_cert)); var_dump($result); echo $result ? 'valid'.PHP_EOL : 'invalid'.PHP_EOL; }php7.4未満のバージョンの場合、phpseclibのv3を使い以下のように書けます。
(あらかじめ) use phpseclib\File\X509; (コード) for($i = 0; $i < (count($pem_from_x5c_cert_list) - 1); $i++) { $cert = $pem_from_x5c_cert_list[$i]; $x509 = new X509(); $x509->loadCA($pem_from_x5c_cert_list[$i+1]); $cert = $x509->loadX509($cert); var_dump($x509->validateSignature()); echo $x509->validateSignature() ? 'valid'.PHP_EOL : 'invalid'.PHP_EOL; }これらの検証が全部okになる必要があります。
まとめ
App Store Server Notificationsの通知のJWT検証をphpで行いました。これらの手順を行ってから通知の中身に入る必要があります。Appleからはv1廃止時期はアナウンスされていませんが今後はv2が必須になりそうなため、早めに対応しておきましょう。参考文献
App Store Server Notifications Version 2(StoreKit 2)の JWS を検証する | by Taiga ASANO | mixi developersValidate StoreKit2 in-app purchase… | Apple Developer Forums
phpでSafetyNet APIのレスポンスの証明書を検証する | GRIPHONE ENGINEER'S BLOG
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。