こんにちは。株式会社Flatt Securityセキュリティエンジニアの村上です。セキュリティ・キャンプ卒業後、新卒入社組としてFlatt Securityでセキュリティエンジニアをしています。
本稿では、BtoCのWebサービスにおいてマーケティング施策として頻繁に発行される「クーポンコード」及び「クーポン機能」のセキュリティ観点について考えたいと思います。様々なサービスが題材として考えられますが、今回はECサイトなどを例に解説していきます。
クーポン機能は割引やポイントの付与など直接的に金銭的な影響に繋がりますが、Googleなどで検索してみると分かる通りどのような対策をすれば良いのかという情報はほとんど知られていません。そこで今回は、クーポン機能を設計・実装する上でSQL Injectionなどの典型的な脆弱性以外でどのような点に気をつければ良いのかについて解説を行います。
Flatt Security Blogではこのように機能にフォーカスしたセキュリティ記事を複数公開しています。ぜひ、あわせてご覧ください。
クーポン機能の仕様とセキュリティ観点
パターンA: 広く公開され一回しか使えないクーポン
デリバリーサービスの「初回購入時3000円引き!」といったクーポンが郵便受けに入っていたりする事はありませんか?
こういった1人1回のみ利用できるクーポンが複数回利用できる場合、不正に無料で宅配サービスを利用され金銭的な損失につながる事は明確です。
このような「広く公開され一回しか使えないクーポン」のセキュリティ観点と対策を考えていきましょう。
観点1: 1度のみ利用の制限回避
クーポンの利用状態をクライアント側で管理していた場合、クーポンコードが入力された場合クライアント側で利用状態を確認し、すでに利用されていた場合はエラーメッセージを表示し購入ボタンを押せなくする事でクーポンの複数回の利用を制限するという実装が考えられます。Webアプリの場合わざわざこのような実装にする人はいないと思いますが、スマホアプリの場合だとやってしまいがちではないでしょうか。
しかし、この場合UI上クーポン利用の制限が行われているだけのため、攻撃者がクーポンを利用するAPIに対して初めてのクーポン利用を装ったリクエストを送信する事によりクーポンを悪用される可能性があります。
対策1: サーバー側でアカウントのクーポン利用状態の管理
クーポンの利用状況をクライアント側で確認する場合上記のような手法で制限を回避される危険性があるため、サーバー側でクーポンの利用状況を確認してください。
当然のことながら、クーポンが利用された場合は適切にその事をデータベースに記載し同じクーポンコードが複数回利用されないようにする必要があります。
なお、クーポンコードがユーザーごとに違う場合、利用済みクーポンコードとの比較だけでは対策として十分ではありません。何らかの手段で手に入れた他人のクーポンコードを入力する事で初回クーポンの特典を複数回利用可能です。そのため、クーポンコードの利用条件を含めた利用状況のチェックを行なってください。
観点2: Race Conditionによる利用回数制限回避
複数の処理が同じデータに同時にアクセスした際に意図しない動作を起こすことをRace Conditionと言います。
これを利用して、同時に複数のクーポン利用のリクエストを送信する事により、クーポンの利用回数制限を回避できる可能性があります。
Race Conditionが発生する原因は、「クーポン利用履歴のチェック」と「クーポンの使用処理」の間にごく短いとはいえ時間差がある事です。
それにより、リクエストAによる「クーポン利用履歴のチェック」と「クーポンの利用履歴をデータベースに書き込む処理」の間に、リクエストBによる「クーポン利用履歴のチェック」が入る事により、リクエストAとリクエストBの両方で「クーポン利用履歴のチェック」の結果が真になります。
対策2: 排他制御を行う
「クーポン利用履歴のチェック」と「クーポンの利用履歴をデータベースに書き込む処理」の間に、異なるリクエストが「クーポン利用履歴のチェック」の処理を行えないようにデータベースに対してロックをかける事により排他制御を行なってください。
パターンB: 公開されていないクーポン
公式LINEを登録している人限定や、アカウント登録している人限定など、一般に公開されていないクーポンコードの取り扱いです。
観点1: クーポンコードの推測
クーポンコードは入力の際の利便性を考えてあまり長すぎない桁数のものが多いです。そのため総当たり攻撃などによりクーポンコードの推測が行われる危険性があります。
対策1-1: クーポンコードの推測困難性を高める
クーポンコードが連番になっている場合や日付+連番のように次のクーポンコードが推測できる場合、第三者によって本来限定された人にしか公開されていないクーポンコードが利用される可能性があります。
英数字混合にしたり、パスフレーズを用いたりすることで推測困難性を高めてください。 他に、クーポンコードの桁数が4桁しかない場合などはクーポンコードの取り得る値の範囲が狭いため、総当たり攻撃等により突破される可能性があります。この場合は推測困難性を高める対応にも限界があるので、対策1-2も行なってください。
クーポンコードが「アカウントID+値」のようなアカウントに紐づくものの場合、クーポンコードの推測困難性は低くても問題ありませんが、クーポンを利用しようとしているアカウントが適切なアカウントであるかを確認してください。
対策1-2: 試行回数制限を設ける試行回数制限を設ける
クーポンコードの入力に何回も連続して失敗した場合など不正な挙動を検知した場合、一時的にアカウントを停止するなどの対策を行なってください。
パターンC: 利用条件のあるクーポン
一般的なクーポンの利用条件として考えられるものをいくつか列挙してみました。これらは一律に同様の対策で不正利用を防ぐことができます。
利用条件1: 他のクーポンと併用できない
クーポンの種類に関わらず併用できない場合や、ドリンク1杯無料と10%引きクーポンの併用はできるが、10%引きクーポンと20%引きといった同種のクーポンの併用はできないといった制限がある場合です。
利用条件2: 有効期限をすぎると利用できない
クーポンに有効期限があり、有効期限をすぎていた場合利用できないといった制限がある場合です。
利用条件3: 購入金額が一定以下だと利用できない
購入金額が10,000円以下の場合利用できないといった、クーポンを利用する際に金額的な制限がある場合です。
対策1: サーバー側での利用条件のチェック
上記のような利用条件が適切であるかはクライアント側ではなくサーバー側で確認してください。
例えばリクエストのデータ構造が以下のようになっている場合、UI上はクーポンの併用は2つまでといった制限がある場合でも配列にクーポンコードを追加する事により3つ以上のクーポンの利用ができるのではないかと推測できます。
クーポンを2つ利用する正常なリクエスト
{ “price”: 19800, “cupon_code”: [ “coupon_A”, “coupon_B” ] }
クーポンコードを3つ送信する場合の不正なリクエスト
{ “price”: 19800, “cupon_code”: [ “coupon_A”, “coupon_B”, “coupon_C” ] }
サーバー側でクーポンコードの個数をチェックせず、単純に配列に入っているクーポンコードを順番に適用する設計になっていると、3つ以上のクーポンを利用する事が可能です。
パターンD: クーポンコードごとに割引が違うクーポン
観点1: 割引率の変更により無料で商品が購入可能
購入フローの設計が適切でない場合、クーポンにより適用された割引率を変更することにより商品を不当に安い値段で購入されてしまう可能性があります。例えば以下のような場合です。
とあるショッピングサイトでは、割引クーポンコードを入力すると注文画面が変化して、「商品」と「割引率」と「割引後の金額」が表示されます。その状態で注文ボタンを押下する事で注文が完了します。
このショッピングサイトを実装するためにどのような設計にするでしょうか?少しわざとらしいですが、「クーポンコードを入力」 -> 「クーポンコードに対応する割引率を取得するAPIを叩く」 -> 「取得した割引率を注文画面で利用」 -> 「「商品情報」と「割引率」を送信し決済終了」という流れにしてみます。
ここまで本稿を読んだ方でしたらほとんどの人が気づいたと思いますが、決済を送信する際に「クーポンコード」ではなくその「割引率」を送信しています。これは、明らかに危険ですね。送信される割引率のパラメータの部分を20から80に変更するだけで本来の商品の値段から80%引きの金額で商品を購入できてしまいます。
対策1: サーバー側でのクーポンの割引率の計算
クーポンの割引率などの情報をAPIなどを利用して取得した場合でも、サーバーに送信する際にはクーポンコードを送信しサーバー側で改めて割引率の計算を行なってください。
終わりに
本稿では「クーポン機能」の仕様におけるセキュリティ上の観点とその対策を紹介しました。一見大した影響が無いように見えるクーポン機能ですが、悪用のしかたによっては金銭的な被害を発生させる事が可能になる事が分かっていただけたでしょうか。
この機能の対策は技術的に高度な対策を行うよりも、丁寧に入力値の検証をサーバーで行う事でほとんどの攻撃を防ぐ事が可能になります。しかし、このような仕様に起因する脆弱性はツールによる診断では発見する事が難しいです。
クーポン機能に限らず、認証・認可のような致命的なリスクにつながりやすい部分も、人間がアプリケーションの期待される挙動と照らし合わせて確認しなければ検出できない脆弱性というのは多く存在します。
このような脆弱性を洗い出すため、弊社Flatt Securityではセキュリティエンジニアの手動検査とツールを組み合わせたセキュリティ診断サービスを提供しています。
認証・認可の観点を重点的に診断した事例のインタビューも公開しております。ご興味のある方は、ぜひご覧ください。
過去に診断を実施したが不安や課題がある、予算やスケジュールに制約がありどのように診断を進めるべきか悩んでいる等、お困り事にあわせて対応策をご提案いたしますので、まずはお気軽にサービスページよりお問い合わせください。
Flatt Securityはセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式Twitterのフォローをぜひお願いします!
ここまでお読みいただきありがとうございました!