チョロすぎて草。これはラムダ技術部さんから拝借しました。汎用性が高くていいフレーズだと思います。
以前、クラッシュローラーというゲームに似たゲームをつくりました。そしてランキング機能を追加する JavaScriptでクラッシュローラーをつくる(6)ではランキング機能も追加しました。
クライアントサイドでは意外と簡単に偽装できる
そしてあるプログラミング実況配信系YouTuberの配信のなかでランキング偽装について話題になりました。
それがこの部分です。
鳩でもわかるC#
改ざんはできないようにしている。
さらには
鳩でもわかるC#
じゃあクラッシュローラーもどきで最高スコア偽造してよ。
なんて強気な発言をしています。
しかしこの発言のあと、実際にある方に最高スコアの偽装をされてしまいます。上記動画の1:02:30あたりを参照。
偽装したことを悪いと言いたいわけではありません。スコアランキングを偽装されても実害はないし、やれるものならやってみろ(=偽装工作に同意している)と言ったのも私自身だからです。
そして見事に偽装されてしまいました。
私の勘違い
そもそもこのゲームのランキング機能ですが、ゲームオーバーになったときに同じディレクトリにあるphpにデータをpostしています。
実際のものとは違いますが、だいたいこんな感じの処理をしています。
JavaScript
1 2 3 4 5 6 7 8 |
function GameOver() { let phpurl = "./save-data.php"; $.post(phpurl, { name:name, score:score, }); } |
save-data.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php saveData(); function saveData(){ // https://lets-csharp.com/* 以外からのリクエストは拒否する // リファラーをチェックすれば問題ないはず・・・実際は違った! $referer = $_SERVER['HTTP_REFERER']; if(strpos($referer,'https://lets-csharp.com/') === false) return; // もしランキング圏内であれば登録する処理をおこなう } |
動画のなかでリファラーをチェックしているからどうのこうの言っているのは別のところでJavaScriptを改ざんしてゲームをしたり、別の方法でsave-data.phpにpostしてもそれはsaveData関数内のif文のところで弾くことができるから偽装はできるはずがないと言っている、というか思い込んでいたわけです。
しかしリファラーは偽装できます。もっといえばリファラーの偽装なんて難しいことをしなくても、もっと簡単な方法で偽装することができるのです。
スコアを偽装する方法
このブログではゲームのソースコードを公開しています。このゲームだとスコアはscoreという変数に格納されていることがわかります。
要はこの変数の値を書き換えることができればよいわけです。そしてあとは適当にゲームをしてゲームオーバーにすれば偽装したスコアが登録できます。スコアを999999999999みたいな点数にして「1位になったぜ。ウェイwww」ということもできるのです。
具体的な方法ですが、
上記のページにアクセスする。
F12キーを押す。すると開発者ツールのウィンドウが表示される。
ゲームスタートのボタンをクリックする。
ゲームをしているときに開発者ツールのコンソールに score=999999999999; と入力してエンターキーを押す。
スコアが999999999999になっていることを確認する。
あとは適当にゲームオーバーになるまでゲームをしましょう。ゲームオーバーになったときにそのときに表示されているスコア(999999999999かそれよりも大きな数)が登録されます。
こんな簡単な方法でランキングを偽装することができるのです。JavaScriptはクライアント側ではこういう変数の書き換えや関数の呼び出しができてしまいます。なのでクライアント側から不正なデータを送らせないような工夫が必要なのですが、いまのところ(2022年4月10日)対応できていません。なのでやりたい放題です。
追記:同じようなゲームをASP.NET Coreで作成しました。これだと偽装はできないはずです。
ランキング偽装可能なゲームのページはこれはこれで残しておこうと思います。ランキングを好きな値に書き換えて遊んでみてください。クライアントサイドではこのような偽装は簡単にできるので注意すべきという反面教師にでもなってくれれば幸いです。
不正に対する対抗手段
偽装対策としては難読化があります。そもそもゲームのソースをそのまま公開するからイタズラをされるのであり、スコアを管理する変数をわかりにくいものにするとか、ソースを読もうとしても読みやすく改行されていなかったりインデントされていないコードになっていて攻撃者の読む気を失せさせるものにするという方法はあります。
難読化で対抗?
ただこれも攻撃者の手を煩わせることはできても、根本的な対策にはなりません。もっともスコアを偽装したところでユーザーの個人情報が流出するとか金銭的な問題が発生するわけではありません。完全にスコア偽装してもいわゆる「誰得」(誰が得するのか?誰も得しない)なのでこれでも充分なのかもしれませんが、不正を完全にシャットアウトするのであれば、キャラクタの描画に関する処理のみをクライアント側JavaScriptでおこない、それ以外の処理はサーバー側でおこなうようにするしかありません。これならサーバーをハッキングされなければランキングを不正に操作することはできません。
ではサーバー側で処理をするのはどうすればよいか?JavaScriptはクライアントサイドだけでなくサーバーサイドでも使えるNode.jsがありますが、実際につかってみるとエラーのたびにサーバーが落ちます。これはさすがに使いにくいです。
初心に返ってC#
ということもあってASP.NET Coreに挑戦してみることにしました。いちおう、鳩でもわかるC#というブログ名にしているからにはC#がメインであるべきです。最近はJavaScriptやphp、フレームワークだとDjangoやLaravelなどC#ではない言語をネタにしたものが多くなってきましたが、ここは初心にかえってC#メインでの情報発信に切り替えていこうかと思います。