フェスティバルで見られるチケットシステムは、次のように機能します。ユーザーがチケットの料金を支払うと、is_scanned
という列を持つデータベースに行が追加されます。この列のデフォルト値はfalseに設定されています。
フェスティバルの警備員がデバイスでバーコード(IDと一意のハッシュを含む)をスキャンするとすぐに、データベースにリクエストが送信され、次のことを確認します。
is_scanned
の値がfalse
に設定されている場合。両方の条件が満たされた場合、値is_scanned
をtrue
、に設定して、他の誰かがチケット/バーコードをコピーしないようにします。
ここでの問題は、スキャンデバイスによってリクエストが送信されてから、値is_scanned
がfalse
からtrue
に切り替わるまでの時間です。
次のシナリオを考えてみます。アリスは支払った有効なチケットを持っていますが、イブにバーコードをコピーさせ、偽のチケットの表示名をアリスからイブに変更します。これで2つのチケットができました。 1つは有効で、もう1つは不正ですが、どちらも同じバーコードを持っています。唯一の違いは名前です。
アリスとイブのチケットがフェスティバルに入ると同時にスキャンされるとどうなりますか?チケットシステムはis_scanned
をtrue
間に合わせて、イブがアリスと同じバーコードで入力できなかったことを確認します。これにより、両方のチケット(有効なチケットと不正なチケット)が「有効」としてガードに表示されます。
もちろん、この種のエクスプロイトは実際には多くの運に依存します。理論的には可能ですが、実際のシナリオでは、これはおそらく失敗します。
しかし、理論的にはこの種のエクスプロイトをどのようにして打ち負かすことができるでしょうか?
私は次の方法を使用してこのエクスプロイトをすでに考慮しています:バーコードがスキャンされると、チケットが有効である(前述の条件を満たしている)場合だけでなく、も表示しますデータベース内の名前。名前がチケットの名前と一致しない場合、チケットが何らかの方法で操作されていることがわかります。また、スキャンデバイスに表示される名前がIDの名前(年齢を証明するためにとにかく全員に提示する必要がある)と一致しない場合も、エントリは許可されません。
このソリューションを迂回する唯一の方法はID詐欺であり、もちろんこれはチケットシステムがチェックする責任を超えています。
これを解決する別の方法は、理論的には、データベース/検証APIに対して行われる各要求の間にランダムな遅延時間を追加することです。この方法では、チケットを同時にスキャンすることはできません...検証の時間が毎回ランダムなミリ秒で遅延されるためです。
次の理由から、私はこれのファンではありません。
is_scanned
をfalse
からtrue
に更新するのに50ミリ秒かかる場合、唯一の解決策は、毎回最小50ミリ秒の間隔でそれを遅延させることです。このエクスプロイトを解決するために他にどのようなソリューションを考えますか?
あなたが説明している脆弱性は 競合状態 です。
対処方法はいくつかありますが、SELECT ... FOR UPDATE
SQLクエリ。選択した行をロックして、現在のトランザクションがコミットされるまで新しい書き込みを防止します。
RDBMSのドキュメントを確認して、正しく実装する方法を確認してください。
ここでの他のソリューションは完全に正しく、それほど簡単ではない大規模なシステムに適しています。
持っているデータを使用すると、それは比較的単純ですが、非ブロッキングオプションを選択できます。
UPDATE [FESTIVAL_TICKET]
SET IS_SCANNED = TRUE
WHERE TICKET_ID = @ScannedKey
AND IS_SCANNED = FALSE
現在、これはアトミック操作です。データベースの2人のユーザーがこれを発行することはできませんおよび行を更新します。 「影響を受けた1行」が返された人(コードでそれを見つける方法はあります。これをテキストで解析しないでください)が入る可能性があります。他の人はステートメントの影響を受けた行をまったく受け取りません。ユーザーフレンドリーにする場合は、IDが間違っているか、スキャン済みかを確認できません。
しかし、重要な詳細は、ステートメントがアトミックであることです。時差がゼロにどれだけ近いかに関係なく、1つだけが勝ちます。読み取りとがなくなったからです書き込み。 1つのアトミック操作で読み取りと書き込みを行うことができます。
これのマイナス面あるように思われるa personは無料で(チケットをコピーして)入手できるかもしれません。
おそらく正しい小さなイベントの場合。
ただし、遅延が多すぎると、何らかの理由でa personに入るリスク以上になります。
デバイスが混雑している場合や速度が遅すぎる場合、チケットスキャナーは数人の追加のユーザーを許可します...なぜなら、ほとんどの人がおそらく有効なチケットを持っているからですよね?
私はこれが今年の主要なイベントで千多数人々が聞いたことのあるミュージシャンのファンが参加した大規模なイベントで起こるのを見ました。
チケット会社は主要な会社(おそらくあなたが働いている会社ですか?)であり、チケットを受け取るためにカスタム構築されたサイトにありました。
私のパーティーはスキャンなしで通過したパーティーの1つでした(そしてはい、私は有効な/法的チケットを持っていました)。
なぜそれが起こったのかを理解する前に、私はそこに立って、チケットの係員を数分見なければなりませんでした。
TL; DR;
すべての不測の事態をコード化しない人々が関与している場合。
2つか3つのナイン(99%〜99.9%)を撮影して、1日と呼びます。
マシンのみが関係する場合のニックピックを保存します...それは、9の束を取得できるときです。
この回答にはすでにすばらしい安価な回答が含まれていますが、ソフトウェアエンジニアリングとセキュリティの両方の観点から独自の回答を追加します。これは、不当に悪用するに関する今後の同様の質問に役立ちます。
理論的には可能ですが...実際のシナリオでは、これはおそらく失敗します。
そして、コストと比較して潜在的な損害は何ですか?追加のセキュリティに費やす労力と頭痛がリスクに値しないことを証明します。
現在、SQLトランザクションの競合状態を適切に処理し、責任/コストをデータベースにシフトするためにすでに提案および承認されているソリューションは、業界標準で最も安価な最良のソリューションです。答えは確かにacceptedだったので、ケースの終わりになる可能性があります。
すでに指摘したように、両方のアテンダントが非常に正確な瞬間にスキャンされ、競合状態の悪用をトリガーするイベントは、数百万数十億でないとしても、数百万の確率で推定できます。 定性的億万長者のオッズについてのアイデアを得るには、 宝くじに関するこの記事 を読み、2番目のリストの上にあるスーパーエナロットでのプレーが2つのチケットをスキャンするのに比べて簡単なゲーム、そして報酬は間違いなく一貫しています。オッズは、脆弱性のexploitabilityを表し、通常は控えめなレベルに限定されます([非常に]可能性が低い、[非常に]可能性が高い)。私は常に、非決定的なセキュリティ関連のイベントを宝くじと比較して、より身近な比較を提供しています。
さらに明確にするために、オッズは次の影響を受けます。
だからここにソフトウェア工学の考慮事項があります。
チケットには価格があるので、Xドルの価値があります。 Xの大きさは50〜100のオーダーになると推定します。脆弱性を悪用して不正に施設に入る人それぞれに、$ Xの損失が適用されます。
より複雑なチェック(パスポート名の制御など)を実装すると、ソフトウェア開発フェーズで必要なコードと、施設に入るのにかかる時間という点で、コストがかかりますboth。警備員は1時間ごとに支払われます。 Ben-Gurionスタイルのセキュリティ監視 *の実装は、正直なところ、はるかにコストがかかり、苦痛です。
さて、あなたは、誰もあなたのシステムを悪用することができないので、よりよく眠りたいと思っています。それはどれくらいしますか?システムを保護するために莫大な金額を支払った後、「保護されていない」システムを実行している競合他社が、数百万を超える確率で80ドルの損失を許容していることに気付く場合があります。その確率を定量化することは困難です。あなたは世界中で最も難しい宝くじに勝つためのより多くのオッズを持っているので、あなたはあなたの仕事を永久に残すことに賭ける方が良いです!
結論:私たちの職業では、勝率は私たちの最高の睡眠パートナーです!
結論2:攻撃者が明らかに自身を同期マイクロ秒にできる自動ネットワークシステムで、競合状態攻撃が可能性が高い悪用される可能性がある!!!それもmultiplicateダメージになる可能性があるので、その場合は最高のセキュリティ対策を歓迎します!
結論3:システムが既に実行されている場合、承認された回答(設計、開発、テスト、UAT、ロールアウト、PMO ...)を使用してパッチを適用する作業is潜在的な損傷よりも費用がかかります。下にコメントしてください
*イスラエルの空港のセキュリティラインは伝説的に長く、徹底的であるため、これを例として引用しました
ここには、データベース部分のエクスプロイトの多くをカバーする良い答えがすでにあります。しかし、イベント(野外フェスティバル)の分野で働いて、チケット検証システムとアプリケーションを設計した私の実生活体験を追加したかったのです。
大きな課題の1つはネットワークの安定性です。これは、すべてのスキャンデバイスが常にネットワーク機能を備えているという仮定がかなり間違っているためです。スキャンプロセス中はいつでも遅延、中断、利用不可が発生する可能性があり、それはお客様のイベントへのエントリーを遅らせるべきではありません(少なくとも私たちの観点から、他のイベントはより厳密な検証が必要な場合があります)。
このアプリケーションでは、チケットは署名を使用して検証されましたが、ネットワークが稼働している場合にのみ、チケットが同期されてデータベースにコミットされました。アプリケーションは、検証済み/コミット予定のチケットをバケットに格納し、ネットワークが利用可能になると、できるだけ多くのチケットをコミットしようとしました。また、チケットごとに1つのINSERT
を実行することも避けます。
最も遠いイベントエントリでは、wifiはまったく到達しませんでした。わずか10メートルのカバレッジで別のルーターを使用するコストを節約するために、スキャンデバイスはルーター間で通信し、接続を共有できます。つまり、デバイスの1つだけがwifiにアクセスできる場合、他のデバイスは理論上、バケット、またはバケットを転送する範囲内の最も近いデバイスにバケットを送信できます。
実際の生活では、ほとんどのスキャンデバイスが少なくとも1分に1回は接続を失っていることを示しています。
したがって、理論的には、1つのチケットは、接続されていないデバイスで必要なだけスキャンできますが、デバイスごとに1回だけです。これは 競合状態 ですが、他の回答が述べていることを利用する方が簡単です。
How to prevent it?
:
ロックが行う並列処理を削除することで、競合状態を回避できます。基本的に、同時書き込みを受け入れるデータベース機能を削減しているため、レイテンシが発生します。
それから問題は、それは価値がありますか?正しい認証を保証するための検証までの遅延を受け入れることはできますか?これは人々が開いたドアを見つけたり、フェンスを飛び越えたりするのを防ぎますか?
良い解決策は、遅延を入れる代わりにメッセージキューを使用することです。スキャンされたチケットはすぐには処理されず、送信されたすべてのチケットが処理される前に待機します。そして、チケットがキューを出て正しく処理されない限り、システムは応答を返しません。それに対する議論は、誰もが他の人が終わるのを待たなければならないので、それは遅いかもしれないということでしょう。ただし、チケットIDを論理的に分割することができます。たとえば、2つのキュー、1つは奇数番号のチケット、1つは偶数番号のチケットです。または、単にID自体にグループ番号を入れるだけです。