ゲーム開発者にとって、チートは永遠の悩みです。どんなに対策しても開発者とチーターは「いたちごっこ」です。今回は初歩的な手段ではありますが、CRCやSHA256といった技術を使ってデータの改ざん、チート行為を検出する方法を紹介します。
# CRC、SHA256について
CRCやSHA256は、データの改変や誤り検知を行うための手法です。
# CRCとは
CRC(Cyclic Redundancy Check)は、送信されたデータに対して剰余を計算して、その結果を誤り検知のための冗長情報として付加することで、受信側での誤り検知を行います。CRCにはいくつかの種類があり、16ビットのCRC16や32ビットのCRC32が一般的に使われています。
# SHA256とは
一方、SHA256は、データの改変を検知するためのハッシュ関数の1つで、入力されたデータから256ビットの固定長の値を生成します。SHA256は、同じデータに対しては必ず同じ値を生成するので、データの改変を検知するために利用されます。SHA256は、セキュリティの強化が必要な場合にも利用されます。
# 実際にコードを書いてみる
Firebaseなどに保存する際データに改変が無いか検出する簡単なサンプルコードを紹介します。
# CRCを用いたチート検出
using System.Security.Cryptography;
using UnityEngine;
public class SaveData
{
// セーブデータの内容を保持するクラス
public int highScore;
public int coin;
public int level;
// セーブデータの内容を文字列化してCRC32ハッシュを計算する関数
public uint CalculateCRC()
{
string saveString = highScore.ToString() + coin.ToString() + level.ToString();
byte[] saveBytes = System.Text.Encoding.UTF8.GetBytes(saveString);
uint crc = Crc32Algorithm.Compute(saveBytes);
return crc;
}
// セーブデータを保存する関数
public void Save()
{
// セーブデータを文字列化してCRC32ハッシュを計算する
uint crc = CalculateCRC();
// CRC32ハッシュをセーブデータに加えてFirebaseに保存する
// (省略)
}
// セーブデータを読み込んでCRC32ハッシュを検証する関数
public bool LoadAndVerify()
{
// Firebaseからセーブデータを読み込む
// (省略)
// セーブデータの内容を文字列化してCRC32ハッシュを計算する
uint crc = CalculateCRC();
// Firebaseから読み込んだCRC32ハッシュと計算したCRC32ハッシュを比較する
uint savedCrc = (uint)loadedData["crc"];
if (crc != savedCrc)
{
// CRC32ハッシュが一致しない場合は改変が検出されたと判断する
Debug.Log("セーブデータが改変されています!");
return false;
}
// セーブデータの内容を読み込む
highScore = (int)loadedData["highScore"];
coin = (int)loadedData["coin"];
level = (int)loadedData["level"];
return true;
}
}
上記の例では、セーブデータの内容を文字列化してCRC32ハッシュを計算し、Firebase等に保存するときにそのハッシュ値も保存しています。そしてセーブデータを読み込むときに、読み込んだデータから再度CRC32ハッシュを計算し、保存されているハッシュ値と比較して一致しない場合は改変が検出されたと判断するという流れになっています。
# SHA256を用いたチート検出
using System.Security.Cryptography;
using UnityEngine;
public class SaveData
{
// セーブデータの内容を保持するクラス
public int highScore;
public int coin;
public int level;
// セーブデータの内容を文字列化してSHA256ハッシュを計算する関数
public string CalculateSHA256()
{
string saveString = highScore.ToString() + coin.ToString() + level.ToString();
byte[] saveBytes = System.Text.Encoding.UTF8.GetBytes(saveString);
SHA256 sha256 = SHA256.Create();
byte[] hashBytes = sha256.ComputeHash(saveBytes);
string hashString = System.Convert.ToBase64String(hashBytes);
return hashString;
}
// セーブデータを保存する関数
public void Save()
{
// セーブデータを文字列化してSHA256ハッシュを計算する
string hashString = CalculateSHA256();
// SHA256ハッシュをセーブデータに加えてFirebaseに保存する
// (省略)
}
// セーブデータを読み込んでSHA256ハッシュを検証する関数
public bool LoadAndVerify()
{
// Firebaseからセーブデータを読み込む
// (省略)
// セーブデータの内容を文字列化してSHA256ハッシュを計算する
string hashString = CalculateSHA256();
// Firebaseから読み込んだSHA256ハッシュと計算したSHA256ハッシュを比較する
string savedHashString = (string)loadedData["sha256"];
if (hashString != savedHashString)
{
// SHA256ハッシュが一致しない場合は改変が検出されたと判断する
Debug.Log("セーブデータが改変されています!");
return false;
}
// セーブデータの内容を読み込む
highScore = (int)loadedData["highScore"];
coin = (int)loadedData["coin"];
level = (int)loadedData["level"];
return true;
}
}
上記の例では、セーブデータの内容を文字列化してSHA256ハッシュを計算し、Firebase等に保存するときにそのハッシュ値も保存しています。そしてセーブデータを読み込むときに、読み込んだデータから再度SHA256ハッシュを計算し、保存されているハッシュ値と比較して一致しない場合は改変が検出されたと判断するという流れになっています。
# さいごに
これらの手法は、ゲームアプリ開発においてセーブデータの改変を防止するために利用されることがあります。ゲームアプリでは、ユーザーがセーブデータを改変することで不正なアイテムやスコアを入手することができます。そのため、セーブデータにCRCやSHA256のような認証情報を追加することで、改変を検知し、不正行為を防止することができます。
ただし、完全に改変を防ぐことはできないため、セーブデータの暗号化やセキュリティ強化など、より高度なセキュリティ対策が必要になる場合もあります。また、セーブデータの改変を検知するための認証情報を保存すること自体が、攻撃者に対してセーブデータの改変を容易にする可能性もあるため、セキュリティ対策の考え方や実装方法には慎重になる必要があります。
これらは初歩的な技術の紹介であり、これらの情報により生じたいかなる損害につきましても当方では責を負いかねますので予めご了承ください。