Skip to main content
舞台裏
舞台裏SecurityPremium

自社の決済システムを監査して見つかったこと

重大な発見は2つ。どちらも、私たちが探そうと思う場所にはありませんでした。すでに修正済みの誤りについての率直な報告です。

公開日 著者: AlphaCloud7 分で読めます

2026年8月、私たちは決済システムを根本から見直しました。何かが起きたからではありません。少し前に稼働を始めたばかりで、何を見落としているのかを知りたかったからです。

見落としはかなりありました。この記事では、その発見を具体的に挙げます。ここに書かれていることはすべて修正済みで、そのほとんどはもともと公開の変更履歴にも載っています。

2つの重大な発見の元になったモデリングの誤り

誰かが私たちから Premium を購入すると、決済プロバイダー側に顧客レコードが作成されます。私たちはそのレコードをサーバー ID で保存していました。Premium はサーバーに付与されるのだから、という理由です。

それはまさに間違ったモデルで、最も深刻な2つの発見の根本原因でした。

顧客レコードは購入者のものです。支払い方法、請求履歴、そして誰かが2つのサーバーの分を支払った時点で、複数のサブスクリプションを持ちます。それをサーバー ID で管理すると、複数の人が同じ行を指すことになります。

発見1:他人の顧客データを、支払いなしで上書きできた

購入はサーバーのどのメンバーにも開かれており、顧客レコードを取得または作成する処理は、支払いの前に実行されます。その処理はサーバー ID で行を見つけ、決済プロバイダー上のメールアドレスも含めて、新しい購入希望者のものへと黙って書き換えていました。

実際には、こういうことです。いつか誰かが購入したことのあるサーバーで1回クリックするだけで十分でした。その後、元の購入者のすべてのサブスクリプションの請求書が、見知らぬ人のメールアドレスに届くようになっていたでしょう。支払いも確認も不要でした。

再利用の前に、そのレコードが本当にこのアカウントのものかを確認するように修正しました。一致しない場合は別のレコードが作成され、もう一方には手を付けません。

発見2:請求ポータルで他人のサブスクリプションが見えた

同じモデリングの誤りが、もう一段先で現れたものです。所有権の確認はサーバーのサブスクリプションに対して行われていましたが、ポータルのセッションは顧客レコードに対して開かれていました。そして顧客レコードは、設計上、複数のサーバーを持つことができます。

そのため、あるサーバーで正当に購入した人なら誰でも、同じレコードがほかのサーバーのために持っているサブスクリプションを閲覧し、管理できてしまいました。

サーバーの行からではなく、具体的な購入から顧客レコードを特定するように修正しました。

コードが私たち自身の規約と矛盾していた2つの発見

これは技術的な問題ではないため、私たちが最も気になったカテゴリーです。

ブラックリストが支払い済みの期間を奪っていた。 私たちの規約では、サブスクリプションは現在の期間の終了時に終わると定めています。ところが、サーバーをブロックするとき、コードは即時解約を呼び出していました。返金もなく、すでに支払われた期間についても、その瞬間からサービスが提供されなくなります。コードは、私たちが書面で約束したこととは別のことをしていたのです。現在は、支払い済みの期間が満了するまで続きます。

撤回権の放棄が証明できなかった。 購入の前に、サービスがすぐに開始されることを確認する必要があります。その確認はブラウザの中だけにあり、送信されることも、どこかに保存されることもありませんでした。立証責任は私たちにあるのに、証拠がまったくなかったのです。現在、同意は決済ルートの必須項目になっており、タイムスタンプとバージョン管理された文言とともに保存されます。文言自体もバージョン管理されています。既存のテキストは決して編集せず、必ず新しいバージョンを作ります。そうしないと、古い記録を再現できなくなるからです。

2回目は1回目よりひどかった

最初の21件の発見のあと、数週間後にもう一度見直しました。それは正しい判断でした。最も深刻な発見は、そのときになって初めて出てきたからです。

サーバー B での返金が、サーバー A の支払い済み永久ライセンスを削除していた。 返金やチャージバックの通知には、支払いしか書かれておらず、サーバーは書かれていません。私たちはそれを顧客レコード経由で特定していました。1回目で、レコードは複数のサーバーにまたがる購入者のものだと正しく確認したことで、まさにその特定方法が一意ではなくなっていたのです。2つのサーバーの分を支払い、片方について返金を受けた人が、もう片方の永久ライセンスを失う可能性がありました。

現在、特定は4つの厳密なステップで行われ、一意に特定できないものは推測せずに報告されます。

勝ったチャージバックが何も元に戻さなかった。 チャージバックのとき、コードは購入の行を削除していました。それは、このサーバーがかつて永久ライセンスを持っていたことを示す唯一の証拠でした。あとで争いに勝っても、元に戻せなかったのです。現在は、削除ではなく印を付けるようにしています。

データ保持期間の処理が、支払い済みのサーバーを削除していた。 Bot がサーバーを去ると、そのデータは30日後に削除されます。この後片付けは、稼働中のサブスクリプションが紐づいているかどうかを一度も確認していませんでした。

そこから得たこと

バグは決済のコードにはなかった。 重大な発見のどれ1つとして、金額、通貨、計算に関するものではありませんでした。すべて、その裏にあるモデルにありました。顧客レコードは誰のものか、支払いは誰のものか、サーバーは誰のものか。金額を見て決済システムを監査する人は、危険な部分から目をそらしていることになります。

監査には2回目が必要。 最も深刻な発見は、1回目の修正の直接の結果でした。これは偶然ではありません。モデルを修正すると、ほかの場所でまだ前提にされている仮定が崩れるのです。

「規約にそう書いてある」は検証できる文。 2つの発見は、私たちが書面で約束したこととコードがしていたこととのずれでした。それは読めば分かることなのに、誰もやりません。エンジニアリングではなく法律のように見えるからです。

購入条件の全文は利用規約に、撤回権については撤回に関するページに記載しています。

その他の記事