web-dev-qa-db-ja.com

FlashゲームのPHPベースのハイスコアテーブルをハッキングする人々を阻止する最良の方法は何ですか

私は、スコアの上限がなく、動きなどをリプレイすることでサーバー上のスコアを検証する方法がないアクションゲームについて話している。

私が本当に必要なのは、Flash/PHPで可能な限り強力な暗号化と、Flashファイル以外でPHPページを呼び出す人々を防ぐ方法です。過去にいくつかの簡単な方法を試しました。 1つのスコアに対して複数の呼び出しを行い、チェックサム/フィボナッチシーケンスなどを完了し、Amayeta SWF EncryptでSWFを難読化しますが、それらはすべて最終的にハッキングされました。

StackOverflowの回答のおかげで、Adobeからさらに情報が得られました- http://www.Adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.html および https:// github .com/mikechambers/as3corelib -暗号化に使用できると思います。ただし、これでCheatEngineを回避できるかどうかはわかりません。

AS2とAS3が異なる場合、AS2とAS3の両方に最適なソリューションを知る必要があります。

主な問題はTamperDataやLiveHTTPヘッダーのようなもののようですが、CheatEngineのように(Mark Websterに感謝)より高度なハッキングツールもあることを理解しています

212
Iain

これは、インターネットゲームとコンテストの古典的な問題です。 Flashコードはユーザーと連携してゲームのスコアを決定します。しかし、ユーザーは信頼されておらず、Flashコードはユーザーのコンピューターで実行されます。あなたはSOLです。攻撃者がハイスコアを偽造するのを防ぐためにできることは何もありません。

  • Flashは、バイトコードが十分に文書化されており、高レベル言語(Actionscript)を記述しているため、思ったよりもリバースエンジニアリングが簡単です。Flashゲームを公開するときは、ソースコードを公開しています。それを知っているかどうか。

  • 攻撃者はFlashインタープリターのランタイムメモリを制御するため、プログラマブルデバッガーの使用方法を知っている人はいつでも変数(現在のスコアを含む)を変更したり、プログラム自体を変更したりできます。

システムに対する最も単純な攻撃は、プロキシを介してゲームのHTTPトラフィックを実行し、ハイスコアの保存をキャッチし、より高いスコアでそれを再生することです。

ゲームの起動時に暗号化されたトークンをクライアントに送信するなど、ゲームの1つのインスタンスに各ハイスコア保存をバインドすることにより、この攻撃をブロックすることができます。

hex-encoding( AES(secret-key-stored-only-on-server, timestamp, user-id, random-number))

(セッションCookieを使用して同じ効果を得ることもできます)。

ゲームコードは、このトークンをハイスコアでサーバーにエコーバックします。ただし、攻撃者はゲームを再び起動してトークンを取得し、そのトークンをすぐにリプレイされたハイスコアのセーブに貼り付けることができます。

次に、トークンまたはセッションCookieだけでなく、高スコア暗号化セッションキーもフィードします。これは128ビットAESキーであり、それ自体がFlashゲームにハードコードされたキーで暗号化されます。

hex-encoding( AES(key-hardcoded-in-flash-game, random-128-bit-key))

これで、ゲームがハイスコアを投稿する前に、ハイスコア暗号化セッションキーを復号化します。これは、ハイスコア暗号化セッションキー復号化キーをFlashバイナリにハードコーディングしたために実行できます。この復号化されたキーと、ハイスコアのSHA1ハッシュを使用して、ハイスコアを暗号化します。

hex-encoding( AES(random-128-bit-key-from-above, high-score, SHA1(high-score)))

サーバー上のPHPコードは、トークンがチェックされ、リクエストが有効なゲームインスタンスからのものであることを確認し、暗号化されたハイスコアを解読し、ハイスコアがハイスコ​​ア(このステップをスキップすると、復号化は単にランダムで、おそらく非常に高い、高いスコアを生成します)。

そのため、攻撃者はFlashコードを逆コンパイルし、すぐにAESコードを見つけます。これは親指のように突き出ていますが、メモリ検索とトレーサーで15分以内に追跡されなかったとしても(このゲームの私のスコアは666なので、メモリ内で666を見つけて、その値に触れる操作をキャッチしましょう。セッションキーを使用すると、攻撃者はFlashコードを実行する必要さえありません。彼女はゲーム起動トークンとセッションキーを取得し、任意の高得点を送り返すことができます。

これで、ほとんどの開発者はあきらめる段階になりました。

  • XOR操作によるAESキーのスクランブル

  • キーを計算する関数でキーバイト配列を置き換える

  • バイナリ全体にわたる偽造キーの暗号化と高得点の散乱。

これはほとんどすべて時間の無駄です。言うまでもなく、SSLもあなたを助けません。 2つのSSLエンドポイントのいずれかが悪ければ、SSLはあなたを保護できません。

実際にハイスコア詐欺を減らすことができるいくつかの事柄は次のとおりです。

  • ゲームをプレイするためにログインを要求し、ログインにセッションCookieを生成させ、同じセッションでの複数の未解決のゲームの起動、または同じユーザーの複数の同時セッションを許可しません。

  • これまでにプレイされた最短の実際のゲームよりも短いゲームセッションのハイスコアを拒否します(より洗練されたアプローチでは、平均ゲーム時間より2標準偏差未満のゲームセッションのハイスコアを「隔離」してみてください)。サーバー側でゲームの継続時間を追跡していることを確認してください。

  • ゲームを1回または2回しかプレイしていないログインのハイスコアを拒否または隔離します。これにより、攻撃者は、作成するログインごとに合理的な外観のゲームプレイの「ペーパートレイル」を作成する必要があります。

  • ゲームプレイ中の「ハートビート」スコア。これにより、サーバーは、1回のゲームプレイの存続期間にわたるスコアの増加を確認できます。妥当なスコア曲線に従わない高スコアを拒否します(たとえば、0から999999にジャンプします)。

  • ゲームプレイ中の「スナップショット」ゲームの状態(弾薬の量、レベル内の位置など)。記録された暫定スコアと後で調整できます。最初からこのデータの異常を検出する方法さえ必要ありません。収集する必要があります。そして、物事が怪しそうに見える場合は、戻って分析できます。

  • セキュリティチェックの1つに失敗したユーザーのアカウントを無効にします(たとえば、検証に失敗した暗号化されたハイスコアを送信することによって)。

ただし、ここではハイスコア詐欺のみを抑止していることに注意してください。 nothingがあると、ifを防ぐことができます。あなたのゲームのラインにお金があれば、誰かが思いついたシステムを打ち負かすでしょう。目的はstopこの攻撃ではありません。ゲームを本当に上手に打ち負かすよりも、攻撃をより高価にすることです。

414
tqbf

間違った質問をしている可能性があります。あなたは、人々がハイスコアのリストを上に向かってゲームをするために使用している方法に焦点を当てているように見えますが、特定の方法をブロックすることはこれまでのところだけです。 TamperDataの経験がないので、話すことができません。

あなたが尋ねるべき質問は、「提出されたスコアが有効で本物であることをどのように確認できますか?」特定の方法はゲームに依存します。非常に単純なパズルゲームの場合、特定の開始状態と終了状態になった一連の動きとともにスコアを送信し、同じ動きを使用してサーバー側でゲームを再実行することができます。指定されたスコアが計算されたスコアと同じであることを確認し、一致する場合にのみスコアを受け入れます。

25
Stephen Deken

これを行う簡単な方法は、ハイスコア値の暗号化ハッシュとそれ自体のスコアを提供することです。たとえば、HTTP GETを介して結果を投稿する場合:http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095

このチェックサムを計算するときは、共有シークレットを使用する必要があります。このシークレットは決してネットワークを介して送信されるべきではありませんが、PHPバックエンドとフラッシュフロントエンドの両方でハードコーディングされるべきです。上記のチェックサムは、文字列 "secret "をスコア"500 "に変換し、md5sumで実行します。

このシステムは、ユーザーが任意のスコアを投稿することを防ぎますが、ユーザーが以前に計算されたスコアとハッシュの組み合わせを再投稿する「リプレイ攻撃」を防ぎません。上記の例では、スコアが500の場合、常に同じハッシュ文字列が生成されます。このリスクの一部は、ハッシュされる文字列により多くの情報(ユーザー名、タイムスタンプ、IPアドレスなど)を組み込むことで軽減できます。これはデータのリプレイを妨げるものではありませんが、データのセットが一度に1人のユーザーに対してのみ有効であることを保証します。

anyリプレイ攻撃の発生を防ぐには、次のような何らかのタイプのチャレンジ/レスポンスシステムを作成する必要があります。

  1. フラッシュゲーム(「クライアント」)は、http://example.com/highscores.phpのHTTP GETを実行しますパラメータなし。このページは2つの値を返します。ランダムに生成されたsalt値と、そのソルト値の暗号化ハッシュと共有シークレットの組み合わせです。このソルト値は、保留中のクエリのローカルデータベースに格納する必要があります。また、おそらく1分後に「期限切れ」にできるように、タイムスタンプを関連付ける必要があります。
  2. フラッシュゲームは、ソルト値と共有シークレットを組み合わせ、ハッシュを計算して、これがサーバーから提供されたものと一致することを確認します。このステップは、ソルト値がサーバーによって実際に生成されたことを検証するため、ユーザーによるソルト値の改ざんを防ぐために必要です。
  3. フラッシュゲームは、ソルト値を共有シークレット、ハイスコア値、およびその他の関連情報(ニックネーム、IP、タイムスタンプ)と組み合わせて、ハッシュを計算します。次に、この情報を、PHPバックエンドにHTTP GETまたはPOSTを介して、ソルト値、ハイスコア、およびその他の情報とともに送り返します。
  4. サーバーは、クライアントと同じ方法で受信した情報を結合し、ハッシュを計算して、クライアントが提供した情報と一致することを確認します。次に、保留クエリリストにリストされているソルト値がまだ有効であることも確認します。これらの条件が両方とも当てはまる場合、ハイスコアをハイスコアテーブルに書き込み、署名された「成功」メッセージをクライアントに返します。また、保留クエリリストからソルト値を削除します。

共有シークレットがユーザーにアクセス可能になると、上記のテクニックのセキュリティが危険にさらされることに注意してください

別の方法として、HTTPSを介してクライアントとサーバーとの通信を強制し、ユーザーがアクセスできる特定の認証局によって署名された証明書のみを信頼するようにクライアントを事前に構成することにより、この前後の一部を回避できます。

18
stormlash

私はtpqfが言ったことを気に入っていますが、不正行為が発見されたときにアカウントを無効にするのではなく、ハニーポットを実装して、ログインするたびにハッキングされたスコアを確認し、トロールとしてマークされたことを疑わないようにします。 「phpBB MOD Troll」のGoogleを使用すると、独創的なアプローチが表示されます。

11
DGM

私は一種の回避策を作りました...私はスコアが増加するギブをしました(あなたは常に+1スコアを取得します)。まず、ランダムなnum(14としましょう)からカウントを開始し、スコアを表示するときに、varからマイナス14のスコアを表示しました。これは、クラッカーが20を探している場合、それを見つけられないためです(メモリ内で34になります)。第二に、私は次のポイントがどうあるべきかを知っているので...次のポイントのハッシュを作成するためにAdobe暗号ライブラリを使用しましたshould be。スコアをインクリメントする必要がある場合、インクリメントされたスコアのハッシュがハッシュと等しいかどうかを確認します。クラッカーがメモリ内のポイントを変更した場合、ハッシュは等しくありません。サーバー側の検証をいくつか実行し、ゲームとPHPで異なるポイントを取得したとき、不正行為が関与していることを知っています。ここに私のコードのスニペットがあります(私はAdobe Crypto libraty MD5クラスとランダム暗号化ソルトを使用しています。callPhp()は私のサーバー側の検証です)

private function addPoint(event:Event = null):void{
            trace("expectedHash: " + expectedHash + "  || new hash: " + MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt) );
            if(expectedHash == MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt)){
                SCORES +=POINT;
                callPhp();
                expectedHash = MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt);
            } else {
                //trace("cheat engine usage");
            }
        }

この手法とSWFの難読化を使用して、クラッカーを停止することができました。また、スコアをサーバー側に送信するときは、独自の小さな暗号化/復号化機能を使用します。このようなもの(サーバー側のコードは含まれていませんが、アルゴリズムを確認してPHPで記述できます):

package  {

    import bassta.utils.Hash;

    public class ScoresEncoder {

        private static var ranChars:Array;
        private static var charsTable:Hash;

        public function ScoresEncoder() {

        }

        public static function init():void{

            ranChars = String("qwertyuiopasdfghjklzxcvbnm").split("")

            charsTable = new Hash({
                "0": "x",
                "1": "f",
                "2": "q",
                "3": "z",
                "4": "a",
                "5": "o",
                "6": "n",
                "7": "p",
                "8": "w",
                "9": "y"

            });

        }

        public static function encodeScore(_s:Number):String{

            var _fin:String = "";

            var scores:String = addLeadingZeros(_s);
            for(var i:uint = 0; i< scores.length; i++){
                //trace( scores.charAt(i) + " - > " + charsTable[ scores.charAt(i) ] );
                _fin += charsTable[ scores.charAt(i) ];
            }

            return _fin;

        }

        public static function decodeScore(_s:String):String{

            var _fin:String = "";

            var decoded:String = _s;

            for(var i:uint = 0; i< decoded.length; i++){
                //trace( decoded.charAt(i) + " - > "  + charsTable.getKey( decoded.charAt(i) ) );
                _fin += charsTable.getKey( decoded.charAt(i) );
            }

            return _fin;

        }

        public static function encodeScoreRand(_s:Number):String{
            var _fin:String = "";

            _fin += generateRandomChars(10) + encodeScore(_s) + generateRandomChars(3)

            return _fin;
        }

        public static function decodeScoreRand(_s:String):Number{

            var decodedString:String = _s;
            var decoded:Number;

            decodedString = decodedString.substring(10,13);         
            decodedString = decodeScore(decodedString);

            decoded = Number(decodedString);

            return decoded;
        }

        public static function generateRandomChars(_length:Number):String{

            var newRandChars:String = "";

            for(var i:uint = 0; i< _length; i++){
                newRandChars+= ranChars[ Math.ceil( Math.random()*ranChars.length-1 )];
            }

            return newRandChars;
        }

        private static function addLeadingZeros(_s:Number):String{

            var _fin:String;

            if(_s < 10 ){
                 _fin = "00" + _s.toString();
            }

            if(_s >= 10 && _s < 99 ) {
                 _fin = "0" + _s.toString();
            }

            if(_s >= 100 ) {
                _fin = _s.toString();
            }           

            return _fin;
        }


    }//end
}

その後、他の偽の変数と変数を送信すると、途中で失われます。小さなフラッシュゲームには多くの作業がありますが、賞金が関係する人は貪欲になります。ヘルプが必要な場合は、PMを書いてください。

乾杯、Ico

受け入れられた回答では、tqbfはスコア変数のメモリ検索を行うことができると述べています(「私のスコアは666なので、メモリ内の数字666を探します」)。

これを回避する方法があります。ここにクラスがあります: http://divillysausages.com/blog/safenumber_and_safeint

基本的に、スコアを保存するオブジェクトがあります。セッターでは、渡された値に乱数(+および-)を乗算し、ゲッターでは、保存された値をランダム乗算器で除算して元の値に戻します。簡単ですが、メモリ検索の停止に役立ちます。

また、ハッキングに対抗するためのさまざまな方法について説明しているPushButtonエンジンの背後にいる人たちのビデオもご覧ください。 http://zaa.tv/2010/12/the-art-of- hacking-flash-games / 。彼らはクラスの背後にあるインスピレーションでした。

4
divillysausages

クライアントが返すデータを信頼することはできません。検証はサーバー側で実行する必要があります。私はゲーム開発者ではありませんが、ビジネスソフトウェアを作成しています。どちらの場合もお金がかかわることがあり、人々はクライアント側の難読化技術を破ります。

定期的にサーバーにデータを送り返し、検証を行うこともあります。アプリケーションが存在する場所であっても、クライアントコードに集中しないでください。

3
zeller

既知の(プライベート)リバーシブルキーを使用した暗号化が最も簡単な方法です。私はすべてがASであるわけではないので、どのような暗号化プロバイダーが存在するのかわかりません。

ただし、ゲームの長さ(再び暗号化されたもの)やクリック数などの変数を含めることができます。

このようなものはすべてcanリバースエンジニアリングされるため、大量のジャンクデータを投入して、人々を香りから追い払うことを検討してください。

編集:いくつかのPHPセッションも。チャプターでゲームを開始してセッションを開始し、(この投稿へのコメントが示すように)時間を記録します。彼らが実際にオープンゲームを持っているかどうかを確認でき、彼らはあまりにも早くまたは大きすぎるスコアを提出していない。

スカラーを計算して、最大スコアがプレイの1秒/分あたりであるかどうかを確認する価値があるかもしれません。

これらのいずれも回避不可能ではありませんが、人々がそれを見ることができるフラッシュにないいくつかのロジックを持つのに役立ちます。

3
Oli

私の経験では、これはプログラミングの問題ではなく、ソーシャルエンジニアリングの問題として対処するのが最適です。不正行為を不可能にすることに焦点を当てるのではなく、不正行為への動機を取り除くことで退屈させることに焦点を合わせます。たとえば、主なインセンティブが公に見える高得点である場合、高得点が表示されたときに遅延をかけるだけで、不正行為者の正のフィードバックループを削除することにより、不正行為を大幅に減らすことができます。

3
Scott Reynen

「クライアントの信頼」問題と呼ばれるものについて話している。クライアント(このキャッシュでは、ブラウザで実行されているSWF)は、設計された動作を実行しているためです。ハイスコ​​アを保存します。

問題は、「スコアを保存」リクエストがフラッシュムービーから来ていることを確認したいということであり、任意のHTTPリクエストではないことです。これの可能な解決策は、高スコアを保存するためにリクエストに付随する必要があるリクエスト時に( flasm を使用して)サーバーによって生成されたトークンをSWFにエンコードすることです。サーバーがそのスコアを保存すると、トークンは期限切れになり、リクエストに使用できなくなります。

これの欠点は、ユーザーがフラッシュムービーのロードごとに1つのハイスコアしか送信できないことです。新しいスコアを再生するには、SWFを更新/再ロードする必要があります。

2
Peter Bailey

新しい人気のあるアーケードmodの方法は、フラッシュからphpにデータを送信し、フラッシュに戻し(またはリロード)、次にphpに戻します。これにより、データを比較したいことは何でもでき、ポストデータ/復号化ハッキングなども回避できます。これを行う1つの方法は、2つのランダム化された値をphpからフラッシュに割り当て(リアルタイムフラッシュデータグラバーを実行しても取得または表示できない)、数式を使用してスコアをランダムな値に追加し、それを使用してチェックすることです最後にphpに移動したときにスコアが一致するかどうかを確認するための同じ式これらのランダムな値は決して表示されず、トランザクションの実行時間も計測します。数秒を超えると、送信を停止してランダム化された値を計算または実行しようとするため、不正行為としてフラグを立てます。スコアの値と比較するための可能なランダム値を返すために、ある種の暗号を介した数値。

あなたが私に尋ねると、これは非常に良い解決策のように思えますが、この方法の使用に関する問題は誰にも見られますか?またはそれを回避する方法はありますか?

2
Vaughn

ゲームが追加されるスコアを登録するたびにRegisterScore(score)のような関数を呼び出すのが最も簡単な方法だと思います。 PHPスクリプトは、適切にデコードする方法を知っています。これにより、スコアを強制しようとすると解凍エラーが発生するため、PHPスクリプトへの呼び出しはすべて停止します。

2
mathieu landry

Flashアプリケーションが暗号化されていない/署名されていないハイスコアデータをネットワーク経由で送信するという事実に基づいてハイスコアシステムが作成された場合、それを傍受および操作/再生できます。その答えは次のとおりです。ハイスコアデータを暗号化(適切に!)または暗号署名します。これにより、少なくとも、SWFファイルから秘密鍵を抽出する必要があるため、人々があなたのハイスコアシステムをクラックするのが難しくなります。多くの人はおそらくそこをあきらめるでしょう。一方、必要なのは、キーを抽出してどこかに投稿するだけです。

実際のソリューションでは、Flashアプリケーションとハイスコアデータベースとの間の通信が増加するため、後者は特定のスコアがある程度現実的であることを確認できます。これはおそらく、どんな種類のゲームを持っているかによっては複雑になります。

2
Jan Krüger

完全にハッキングできないようにする方法はありません。SWFを簡単に逆コンパイルできるため、熟練した開発者のハッカーがコードをトレースして、使用する暗号化システムをバイパスする方法を見つけることができます。

ただし、TamperDataなどのシンプルなツールを使用して子供の不正行為を止めたい場合は、起動時にSWFに渡す暗号化キーを生成できます。次に、 http://code.google.com/p/as3crypto/ のようなものを使用して、ハイスコアを暗号化してからPHPコードに渡します。データベースに保存する前にサーバー側でそれを行います。

2
David Arno

私は通常、ゲームセッションの「ゴーストデータ」をハイスコアエントリに含めます。したがって、レースゲームを作成する場合は、リプレイデータを含めます。多くの場合、リプレイ機能またはゴーストレーシング機能(最後のレースと対戦するか、リーダーボードの男#14のゴーストと対戦する)のリプレイデータを既に持っています。

これらのチェックは非常に手作業ですが、目標がコンテストの上位10エントリが合法であるかどうかを確認することである場合、これは他の人が既に指摘したセキュリティ対策の備蓄に役立つ追加機能です。

目標が、ハイスコアのリストを誰も見る必要なしに時間の終わりまでオンラインに保つことである場合、これはあなたに多くをもたらしません。

2
Lucas Meijer

サーバー側のすべてのゲームロジックを維持することによってのみ可能です。これは、ユーザーの知識がなくてもスコアを内部に保存します。経済的および科学的な理由から、人類はこの理論をターンベースを除くすべてのゲームタイプに適用することはできません。たとえばサーバー側で物理を維持することは計算コストが高く、手の速度として応答するのが困難です。チェスをプレイしている間でも、AIチェスのゲームプレイを対戦相手と一致させることができます。したがって、より良いマルチプレイヤーゲームには、オンデマンドの創造性も含まれている必要があります。

2
placeohlder

あなたが望むものを達成することは本当に不可能です。特に CheatEngine のような使用方法を知っている場合、Flashアプリの内部は常に部分的にアクセス可能です。つまり、Webサイトとブラウザ<->サーバー通信がどれだけ安全であっても、比較的簡単に克服できます。

1
Mark Webster

[〜#〜] amfphp [〜#〜] を介してバックエンドと通信することをお勧めします。少なくとも怠け者は、ブラウザコンソール経由で結果をプッシュしようとしないようにする必要があります。

1
m1gu3l