web-dev-qa-db-ja.com

クライアント側のハイスコアをサーバーに安全に送信する

単純なJavaScriptゲームを組み込んだWebアプリケーションを作成しています。プレーヤーがゲームのプレイを終了すると、ハイスコアがサーバーに送信され、保存されます。

特定の期間の後、最高のスコアを持つプレーヤーは賞品を受け取ります。

安全にハイスコアを送信し、クライアントが「誤った」ハイスコアを送信しないようにする方法はありますか?

現在使用しているもの:

  • Https。
  • 各ゲームの前にサーバーランダムトークンを取得し、ゲーム終了後にスコアと一緒に送信します。
42

サーバーはクライアントから受信するデータを完全に信頼できないため、ハイスコアの検証は困難です。

ここにいくつかのオプションがあります:

  1. クライアント側のコードとサーバーへのトラフィックを難読化します。これは最も簡単なオプションです-チートすることは可能ですが、おそらく誰の価値もないでしょう。

  2. 検証のために、ゲームの全部または一部のリプレイをサーバーに送信します。プレーヤーの動きをサーバー上で実行して、正当なスコアを決定できます。これは一部のゲームでは機能し、他のゲームでは機能しません。

  3. ゲームのロジックと検証をサーバーに移動します。クライアントは各移動をサーバーに中継するだけで、サーバーは検証され、ゲームの状態を更新します。

それはあなたのゲームがどのように機能するか、人々が不正行為をする可能性がどの程度あるか、そしてあなたが彼らがそうするならどれだけあなたが気にするかにかかっています。

113
grc

攻撃者がネットワーク層の中間者攻撃者である場合、httpsで十分です。しかし、残念ながら攻撃者はクライアントなので、これはかなり無意味です。

チートプルーフゲームのルールナンバー1:クライアントを信頼しないでください!クライアントは敵の手中にあります。

JavaScriptゲームは、ユーザーのWebブラウザーで実行されます。 Web開発者としてご存じのとおり、今日のすべてのWebブラウザーには、アプリケーションの実行中にすべての変数を表示および変更できる組み込みデバッガーが付属しています。ユーザーは、デバッガーを使用して、提出前にスコアを変更できます。それを防ぐためにできることは何もありません。ブラウザベースのゲームでは、サードパーティ製のアンチチートツールを使用することもできません(これらは疑わしい価値があり、とにかく倫理的に疑わしいものです)。

唯一の対策は、サーバー上で操作する価値のあるすべてのゲームメカニズムを実装することです。クライアントはユーザーのコマンドをサーバーに転送し、サーバーのメッセージに基づいてゲームプレイを視覚化するだけです。実際のゲームプレイは、詐欺師の手の届かないサーバー上で発生するはずです。

はい。これは、ゲームのソフトウェアアーキテクチャをゼロから再設計する必要があることを意味します。また、ネットワークラグが見えにくくなるように、予測コードと補間コードを追加する必要があります。そして、それはあなたがあなたのサーバーのためにはるかに良いサーバーハードウェアとより良いインターネット接続を必要とするであろうことも意味します。しかし、チートフリーのオンラインゲームが必要な場合は、それが唯一の選択肢です。

29
Philipp

安全なゲームのハイスコアを達成するために必要なのは、「 作業の証明 」、またはより適切にはプレイの証明と呼ばれるものです。

作業/プレイの証明は、ゲーム終了前に計算するのが難しいが、ゲーム終了後に計算するのが簡単で、サーバーによる検証が容易なデータです。たとえば、数独ゲームの場合、作業の証明はパズルの解決策です。

残念ながら、多くのゲームに適切なプレイの証明システムを統合する一般的な方法はありません。ゲームのプレイを微調整したり再調整したり、リーダーボードの範囲を十分に安全なプレイの証明を含めることができる要素のみに制限したりせずに、プレイの証明を統合できるとは限りません。

ゲームでのプレイの証明は、サーバーがゲームのRNGのシードを発行することから始まります。次に、クライアントは、プレーヤーが発行されたシードと一致するプレーの証拠を見つける必要があります。多くのゲームでは、プレイの証拠は、ハイスコアサブミッションでサーバーにゲームのソリューションを提出することによって形成できます。他のゲームでは、ゲームとその証拠に応じて、ゲームセッション全体をさまざまな詳細レベルで記録する必要がある場合があります。あなたが使っている遊び。

プレイの証拠は、リプレイ耐性を備えている必要があります(1人のプレイヤーは、他のプレイヤーの完成したゲームを使用して、自分のプレイの証拠を計算しやすくすることはできません)。これは、プレーの証拠を、プレーヤーが別のプレーヤーのプレーの証拠を再利用できないだけのランダム性があるゲームに限定しますが、プレーの検証が不可能になるほど多くの非決定論的な動作を持つこともできません。

プレイの証拠も限られています。たとえば、数独ゲームでは、プレイヤーがパズルを解くのにかかる時間を証明することはできません。プレイの証拠は、プレイヤー自身がプレイしたゲームと、自分のためにゲームをプレイするスクリプトを書いたプレイヤーとを常に区別できるわけではありません。

17
Lie Ryan

この問題にはある種の解決策があることをお伝えしたいと思いますが、それはあなたには利用できない可能性が高いです。ソリューションは「準同型暗号化」と呼ばれ、クライアントは、どの値を使用しているかを正確に知らなくても既知の計算を実行でき、サーバーは計算値の構造をチェックして、クライアントが単にランダムな文字列を送り返しますが、実際には提供されたいくつかの値からそれを構築しました。

問題はslowまたはincompleteのいずれかであり、「incomplete」は「論理プリミティブの全範囲を組み込んでいない」ことを意味します。多くの場合、これは一部の暗号化および復号化操作E()、D()に加えて、次のような複雑な操作を許可する部分的なシステムです

D(E(A)⊕E(B)) = A + B、

など。

それでは、これがいくつかのゲームの問題をどのように解決するか見てみましょう。六角形のグリッドの[〜#〜] n [〜#〜]ヘックスを押している「消灯」タイプのゲームを考えて、それぞれがその状態と周囲の状態の両方を切り替えるそれ。これは、これらの「プレス」の可換代数であり、したがって、特定のソリューションではプレスの合計数はNを超えません。さらに、各ヘクスは、初期状態と、自身のヘクスのプレスとその周囲の6つのヘクスにのみ依存します。

私たちの準同型暗号化スキームはXORではなく+のみを許可するので、フリップされた回数ごとに各ヘックスに3ビットカウンターを与えます。 (クライアントソフトウェアは、ヘックスを2回押すたびに1回だけに自動的に縮小します。)したがって、実際のフリップアクションは、次のようなビットベクトルです。

001 001 000 000 000 001 001 001 000 001 001 000 000 ... 000 00000000 00000001

つまり、反転するこれらの3ビットフィールドのそれぞれに1があり、16ビットカウンターにも1があります。

これらすべてを準同型暗号化スキームで暗号化し、それぞれをクライアントに送信します。クライアントは、返されたこれらの暗号化された値から計算された暗号化された値を返します。次に、これを復号化し、復号化した値とビット文字列をANDで結合します。

001 001 001 001 ... 001 11111111 00000000

そして、これらの8つのカウンタービットの0に隣接する初期ゲーム状態と比較します。

彼らが私たちにランダムな値を送信した場合、それを受け入れる可能性は2です。-(N + 8) したがって、テストに合格するための唯一の便利な方法は、許可されたいくつかの組み合わせで与えた値を使用することです。整数のオーバーフローのために直接許可しないいくつかの動きにアクセスできますが、フィールドを3ビットよりも広くして、右側のカウンターのコストを高くすることができます。しかし、個別のボタン操作が送信されることはなく、履歴の再生がはるかに少なくなりました。「グリッドを反転させる方法は次のとおりです」というベクトルを受け入れました。これは、「超安全でないこと」であり、誰もが警告していますが、秘密鍵にアクセスすることなしに私たちが心配していることを彼らがすることができないような方法でそれをしました。

代わりに、暗号化投票でこの種のことを見て、投票マシンがアリスに1000票を自発的に与えないことを保証したいとします。

8
CR Drost

クライアント側のすべてがなりすまされる可能性があります。だから答えはノーです。

[〜#〜] edit [〜#〜]ここで書いたセキュリティメカニズムは削除しました。これは、正当なセッションのみを保証するためです...しかし、偽のスコアは、正当なセッションを通じて送信できます。ありがとう@ルークパーク

5
OscarAkaElvis

私の推奨事項は、難読化の手法の詳細であり、多くのユーザーにとって苛立たしいハードルになる可能性があります。ただし、上級ユーザーまたは熱心なユーザーでも、ソースコードを分析して結果を再現できます。

Hashids のようなツールを使用してスコアのハッシュを作成し、これをプレーンテキストスコアと共にサーバーリクエストで送信します。ユーザーIDの文字列値は、スコアのハッシュをエンコードするために使用されるソルトである可能性があり、ハッシュが共有されている場合は役に立たなくなります。サーバー側では、このハッシュをデコードし、その結果を送信された平文スコアと比較して、期待どおりに一致することを確認できます。

0
Ben Harrison