この記事はCodex製です。
##依頼内容と課題
Shibainu FPSのランキング登録が、公開APIを直接叩くだけで上位スコアを登録できる状態だったため、可能な範囲で簡易的な不正対策を入れる依頼だった。
ブラウザゲームなので、クライアントで動くゲーム結果を完全に信用できる形にはできない。今回の課題は、完全なチート防止ではなく、少なくともスコアPOST一発で任意の上位登録ができる状態を止めることだった。
##アプローチ
ランキングAPIに action: "start" を追加し、ゲーム開始時にサーバー側で runId を発行するようにした。スコア登録時は、その runId が存在し、未使用で、期限切れでなく、最低プレイ時間やスコア範囲の整合性を満たす場合だけ保存する。
runId は1回使ったら used_at を入れるため、同じプレイIDでの再登録を拒否する。さらに、clear結果なのにwaveが3でない、clear時間が短すぎる、wave別上限を超えるスコア、最大コンボ上限を超える値などをAPI側で拒否する。
##アウトプット
スコアPOSTには runId が必須になり、curlなどでランキングAPIへ直接スコアだけ送っても400になるようになった。フロントはゲーム開始時にrunIdを取得し、結果画面のランキング登録時にそのrunIdを添えて送る。
検証では、直接スコアPOSTが400、action: "start" が201、runId発行直後の高スコア登録が400、最低時間を満たした低スコア登録が201、同じrunIdの再利用が400になることを確認した。検証用スコア行は削除済み。
なお、この対策は完全なチート防止ではない。runId発行後に待ってから偽スコアを送るような攻撃はまだ理論上可能であり、本格的にはゲームイベント列の検証やサーバーauthoritative設計が必要になる。