ハイスコアが入った小さな .txt ファイルを保存するプログラムがあります:
// Create a file to write to.
string createHighscore = _higscore + Environment.NewLine;
File.WriteAllText(path, createText);
// Open the file to read from.
string createHighscore = File.ReadAllText(path);
問題は、ユーザーがテキストエディタを使ってできるだけ簡単にファイルを編集できることです。そこで、ファイルを読み取り/編集不可または暗号化不可。
データをリソース ファイルに保存できると考えていましたが、リソース ファイルに書き込むことはできますか? または、.dll として保存し、暗号化/復号化したり、MD5 サム/ハッシュを探したりすることもできます。
ベストアンサー1
ユーザーによるファイルの変更を阻止することはできません。それはユーザーのコンピュータなので、何でも好きなことができます (これが DRM の問題全体が…難しい理由です)。
ハイスコアを保存するためにファイルを使用しているとおっしゃったので、いくつかの代替手段があります。前述のように、本当に執念深い攻撃者が値を改ざんするのを阻止する方法はないことに注意してください。アプリケーションはユーザーのコンピューターで実行されているため、攻撃者はそれを逆コンパイルし、値をどのように保護しているかを確認し (プロセスで使用される秘密にアクセスし)、それに応じて行動することができます。ただし、アプリケーションを逆コンパイルし、使用されている保護スキームを調べ、スクリプト/パッチを作成して回避し、自分だけが確認できる数字を変更するつもりなら、実行してもいいのではないでしょうか。
コンテンツを難読化する
これにより、ユーザーがファイルを直接編集することはできなくなりますが、難読化アルゴリズムが判明すると、編集を阻止できなくなります。
var plaintext = Encoding.UTF8.GetBytes("Hello, world.");
var encodedtext = Convert.ToBase64String(plaintext);
暗号文をファイルに保存し、ファイルを読み取るときにプロセスを逆にします。
コンテンツに署名する
これにより、ユーザーがファイルを編集したり、その内容を表示したりすることを防ぐことはできません (ただし、ハイスコアは秘密ではないので、気にする必要はありません)。ただし、ユーザーがファイルを改ざんしたかどうかを検出できるようになります。
var key = Encoding.UTF8.GetBytes("My secret key");
using (var algorithm = new HMACSHA512(key))
{
var payload = Encoding.UTF8.GetBytes("Hello, world.");
var binaryHash = algorithm.ComputeHash(payload);
var stringHash = Convert.ToBase64String(binaryHash);
}
ペイロードとハッシュの両方をファイルに保存し、ファイルを読み取るときに、保存されたハッシュが新しく計算されたハッシュと一致するかどうかを確認します。キーは秘密にしておく必要があります。
コンテンツを暗号化する
.NET の暗号化ライブラリを活用して、コンテンツを保存する前に暗号化し、ファイルを読み取るときに復号化します。
次の例を鵜呑みにせず、実装する前にすべての動作を理解するために十分な時間をかけてください (はい、あなたは些細な理由でそれを使用しますが、将来のあなた、または他の誰かがそうしないかもしれません)。IV とキーを生成する方法には特に注意してください。
// The initialization vector MUST be changed every time a plaintext is encrypted.
// The initialization vector MUST NOT be reused a second time.
// The initialization vector CAN be saved along the ciphertext.
// See https://en.wikipedia.org/wiki/Initialization_vector for more information.
var iv = Convert.FromBase64String("9iAwvNddQvAAfLSJb+JG1A==");
// The encryption key CAN be the same for every encryption.
// The encryption key MUST NOT be saved along the ciphertext.
var key = Convert.FromBase64String("UN8/gxM+6fGD7CdAGLhgnrF0S35qQ88p+Sr9k1tzKpM=");
using (var algorithm = new AesManaged())
{
algorithm.IV = iv;
algorithm.Key = key;
byte[] ciphertext;
using (var memoryStream = new MemoryStream())
{
using (var encryptor = algorithm.CreateEncryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (var streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write("MySuperSecretHighScore");
}
}
}
ciphertext = memoryStream.ToArray();
}
// Now you can serialize the ciphertext however you like.
// Do remember to tag along the initialization vector,
// otherwise you'll never be able to decrypt it.
// In a real world implementation you should set algorithm.IV,
// algorithm.Key and ciphertext, since this is an example we're
// re-using the existing variables.
using (var memoryStream = new MemoryStream(ciphertext))
{
using (var decryptor = algorithm.CreateDecryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (var streamReader = new StreamReader(cryptoStream))
{
// You have your "MySuperSecretHighScore" back.
var plaintext = streamReader.ReadToEnd();
}
}
}
}
}