質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.32%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

App Store

App Storeは、Apple社が運営する、iPhone、iPod touch、iPad向けアプリケーションソフトのダウンロードサービスです。携帯電話、Wi-Fiによる無線通信に対応しており、多くのアプリケーションをダウンロード、インストールすることができます。世界中の開発者によってアプリケーションが登録されており、有償のソフトもあればフリーソフトも多く登録されています。

意見交換

4回答

311閲覧

iOSアプリ課金のサーバーサイド処理で、アプリから送られたトランザクションIDを信用して良い?

Fushihara

総合スコア52

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

App Store

App Storeは、Apple社が運営する、iPhone、iPod touch、iPad向けアプリケーションソフトのダウンロードサービスです。携帯電話、Wi-Fiによる無線通信に対応しており、多くのアプリケーションをダウンロード、インストールすることができます。世界中の開発者によってアプリケーションが登録されており、有償のソフトもあればフリーソフトも多く登録されています。

0グッド

1クリップ

投稿2025/03/01 16:06

編集2025/03/01 16:35

テーマ、知りたいこと

iOSのサブスクのアプリ課金を実装しています。
アプリで課金操作を行った後にアプリサーバーにその課金情報を反映させます。
verifyReceipt APIが非推奨になっているので、代替の手段を知りたいです。言語はnodejs

この質問での「課金のレシート」はMII~ から始まるASN.1のbase64です。jwt ではありません。


今まではアプリで課金操作をした後にStoreKitから受け取ったレシートをそのままアプリサーバーに投げて、そのアプリサーバーからverifyReceipt APIにそのまま投げてレシートのjsonを取得していました。
悪意あるユーザーがデタラメなレシートを送ったとしても、そのレシートはAppleの署名がされていないので verifyreceipt APIでエラーになって気づく事が出来ました。

しかしverifyreceipt APIは非推奨になり、いつ消えるか分からない状態です。
https://developer.apple.com/documentation/appstorereceipts/verifyreceipt


色々な記事を見ると「サーバーにトランザクションIDを送り、サーバー側ではAppStoreServerAPIを使ってトランザクションの詳細情報を取得する」という方法が代替案として提示されていて
https://speakerdeck.com/yuheiito/storekit2woshi-tutake-jin-sisutemunohururiniyuaru?slide=29

Apple公式の Apple App Store Server Node.js Library でも
https://github.com/apple/app-store-server-library-node
レシート検証の処理は以下の用に記述されています。

const appReceipt = "MI..." const receiptUtil = new ReceiptUtility() const transactionId = receiptUtil.extractTransactionIdFromAppReceipt(appReceipt)

このどちらの方法も「デタラメのトランザクションIDをアプリサーバーにPOSTされたら無課金でサブスクに加入されてしまうのでは?」という不安があります。
MII~ から始まる文字列のレシートはASN.1という形式で、特定のプロパティのみデタラメな値に差し替える事は可能なはずです。

Apple公式のnodejsのライブラリの該当処理のロジックを追いかけたのですが、レシートのbase64からトランザクションIDのプロパティを読み込んでいるだけでAppleの証明書の検証はしていません。
また、前者のspeakerdeckの例においては「サーバーにトランザクションIDを送る」と書いているのでトランザクションIDの数字を信じるしかない状態だと思います。


これ、アプリから渡されたトランザクションIDの数字をそのまま信用してよいのでしょうか?
アプリを解析とかiOSに中間者攻撃を仕掛ければアプリサーバーへの通信内容は丸裸ですし、
そこで不正に入手されたトランザクションIDを投げ込まれたらアプリサーバーはそれを受け入れてしまうはずです。


StoreKit2の jwsRepresentation というプロパティを使えばjwsが取れて、
jwsは一般的な方法で証明書の検証が出来ると思いますが、flutterを使っているのでこの方法は現状使えません。
あくまでアプリから送られてくるのはMII~ から始まるレシートのbase64から変更出来ない想定です。
https://developer.apple.com/documentation/storekit/verificationresult/jwsrepresentation-21vgo

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

回答4

#1

utm.

総合スコア673

投稿2025/03/03 01:14

回答がついていないので一般論として回答させていただきますが、

アプリを解析とかiOSに中間者攻撃を仕掛ければアプリサーバーへの通信内容は丸裸ですし、

と仰っていますが、改変や盗聴が容易な通信プロトコルを使用していること自体が、セキュリティ的におかしいと思うのですが、そこが問題なのでは。。。?

このどちらの方法も「デタラメのトランザクションIDをアプリサーバーにPOSTされたら無課金でサブスクに加入されてしまうのでは?」という不安があります。

アプリサーバー側で当然appleの決済システムに登録等されているのかの検証を行うと思うのですが。。。?
その際に、デタラメであったら検証できないので当然エラーなどになって加入不可能なシステムにするのが普通では?

StoreKit
https://developer.apple.com/jp/documentation/storekit/

App Store Server API
https://developer.apple.com/documentation/appstoreserverapi/

色々な記事を見ると「サーバーにトランザクションIDを送り、サーバー側ではAppStoreServerAPIを使ってトランザクションの詳細情報を取得する」という方法が代替案として提示されていて
https://speakerdeck.com/yuheiito/storekit2woshi-tutake-jin-sisutemunohururiniyuaru?slide=29

引用先を見るにトランザクションIDから検証するので、ほかのユーザーに成りすますにはこのトランザクションIDが必要になるという話ですよね。

Appleの証明書の検証はしていません

どういうコンテキストでの話か明確でないですが、正しいAPIを使っているかどうかは諸に開発者(あなた?)の責任では?

これ、アプリから渡されたトランザクションIDの数字をそのまま信用してよいのでしょうか?

端末自体が物理的に盗まれるなどを懸念しているのでしたら、それはエンジニアの過失ではなくそのユーザーの問題ですから、ご利用端末にパスワードかけてくださいくらいしか言えることがないかと思います。


蛇足
ロジックのフローを存じ上げませんが、スマートフォンで決済してappleに購入しました的な処理をした後に、その端末からappleに購入したかを問い合わせたいこともあるでしょうし、その問い合わせを当然今回のようにアプリケーションサーバーから確認したい場合は、アプリケーションサーバーから確認するというだけという話ではないのですか?

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#2

Fushihara

総合スコア52

投稿2025/03/08 10:22

ご指摘ありがとうございます、勉強になります。

「デタラメのトランザクションID」という表記がよろしく無かったみたいで失礼しました。
自分が心配している不正利用の想定は以下のような感じです

  • 正しいアプリ利用者のAさんがサブスクを購入した。
  • Aさんの購入情報のトランザクションIDが第三者に漏れてしまった。Aさんが意図したかしてないかは関係なく。
  • 不正利用しようとしているBさんが、既存の別の課金のレシートに、AさんのトランザクションIDを埋め込んだ不正なレシートを作成します。
    • 質問に記載している通り、レシートの形式はASN.1というバイナリ方式なので、既存のレシートからトランザクションIDの値を書き換える事は可能のはずです
  • その不正作成したレシートをアプリサーバーにPOSTされた。

この時、アプリサーバーはそのレシートが不正だと見抜くことが出来ないのでは? という質問内容です。

以前のverityReceiptの形式ですと、トランザクションIDを不正に加工したレシートを弾いてくれました。
それが出来なくなっちゃってるけど、それはアプリ開発者の皆さんは気にしていないのですか?
という事です。

恐らくみなさん全く気にしていないみたいで、多分自分の心配がどこか間違っているのだろうと思いますが
どこがおかしいかをご享受いただけたらと思います。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#3

utm.

総合スコア673

投稿2025/03/08 11:54

うーん。こちらとしても多分読解の方法が微妙に間違っているのかもしれないのですが、例えばスマホでPayPay支払いできるとして、
スマホを盗まれて、PayPayの残高を不正利用されたとしたら、PayPayのサーバーはシステム的にそれを検知することは出来ないですよね?

トランザクションIDが盗まれるというのはそれと全く同義なのではないでしょうか?
(別に当然ですが、PayPayでなくてもYouTubePremiumとかAmazonプライムとかでも同じ事です。)

GoogleのアカウントがだとかTwitterのアカウントだとかが盗まれたらそのアカウントのサブスクリプションはそりゃ当然利用されるわけで...

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#4

utm.

総合スコア673

投稿2025/03/08 12:48

一度自分で偽造してみて、試してみるとよいのではないでしょうか?それで何となく質問の意図が明確になるかもしれません。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

この意見交換はまだ受付中です。

会員登録して回答してみよう

アカウントをお持ちの方は

関連した質問