web-dev-qa-db-ja.com

.txtファイルを読み取り不可/編集不可にする

ハイスコ​​アを含む小さな.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-sum/hashを探します。

36
Daniel Kng

ユーザーがファイルを変更するのを防ぐことはできません。それは彼らのコンピューターなので、彼らは何でもやりたいことができます(だから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();
                }
            }
        }
    }
}
59
Albireo

比較的低いセキュリティを探しているように思えるので、実際にはチェックサムに行くことをお勧めします。いくつかの擬似コード:

string toWrite = score + "|" + md5(score+"myKey") + Environment.NewLine

スコアが100の場合、これは次のようになります。

100 | a6b6b0a8e56e42d8dac51a4812def434

ユーザーがファイルを気にしないようにするには、次を使用できます。

string[] split = readString().split("|");
if (split[1] != md5(split[0]+"myKey")){
     alert("No messing with the scores!");
}else{
     alert("Your score is "+split[0]);
}

もちろん、誰かがあなたの鍵を知るとすぐに、彼らは望むものをこれで台無しにすることができますが、私はそれをこの質問の範囲を超えて検討します。同じリスクは、暗号化/復号化メカニズムにも適用されます。

下のコメントで言及されている問題の1つは、誰かが(ブルートフォーシングによって)鍵を見つけたら、それを共有でき、誰もがファイルを非常に簡単に変更できることです。これを解決する方法は、コンピューター固有のものをキーに追加することです。たとえば、ログインしたユーザーの名前は、md5を実行しました。

string toWrite = score + "|" + md5(score+"myKey"+md5(System.username /**or so**/)) + Environment.NewLine

これにより、キーが「単純に共有される」ことを防ぎます。

12

おそらくあなたの最善の策は、標準のNTセキュリティを使用してファイル全体を保護し、プログラムでアクセス制御リストを変更して、ファイル全体が不要なユーザーによって編集されないようにすることです(もちろん、自分のアプリケーションになりすます人を除く)。

暗号化はここでは役に立ちません。ファイルは通常のテキストエディター(たとえば、notepad)を使用して編集可能であり、エンドユーザーは余分な文字を追加するだけでファイルを破損する可能性があるためです。

プログラミング作業を伴わない代替アプローチがあります...

テキストファイル全体を手動で編集すると、サポートが失われたことをユーザーに伝えます。結局のところ、このデータを保存しているのは、アプリケーションに必要なためです。破損したり、手動で編集するという危険なタスクを実行すると、アプリケーションでエラーが発生する可能性があります。

プログラミング作業を伴う別の代替アプローチ...

アプリケーションからファイルを変更するたびに、MD5またはSHAハッシュを計算して別のファイルに保存できます。もう一度読み書きしたい場合は、チェックします。ファイル全体が同じハッシュを生成してから再度書き込みます。

このように、ユーザーはファイルを手動で編集できますが、ユーザーがこのnexpected動作をいつ実行したかがわかります(ファイルが変更されるたびにユーザーが手動でハッシュを計算しない限り... )。

9

まだ言及していないことは、オンラインリーダーボードにハイスコアを保存することです。明らかにこのソリューションにはさらに多くの開発が必要ですが、ゲームについて話しているので、おそらくSteam、Origin、Uplayなどのサードパーティプロバイダーを利用できます。あなたのマシン。

6
Nzall

Dllにデータを保存することはできません。また、リソースファイルとtxtファイルの両方が編集可能です。暗号化があなたにとって唯一の方法のようです。文字列を暗号化してから、txtファイルに保存できます。このスレッドを見てください: 文字列の暗号化と復号化

5

シンプルなソリューション:

別の解決策:
SQLite DBにデータを書き込みますか?

2
Eugene Krapivin

CryptoStream で暗号化してシリアル化および逆シリアル化できます:

ファイルのシリアル化:

  • 書き込みモードでFileStreamを作成して開きます
  • 暗号ストリームを作成し、ファイルストリームを渡します
  • コンテンツをCryptostreamに書き込む(暗号化)

ファイルのデシリアライズ:

  • 読み取りモードでFileStreamを作成して開く
  • 暗号ストリームを作成し、ファイルストリームを渡します
  • Cryptostreamから読み取る(復号化)

ここで例と詳細情報を見つけることができます:

msdn.Microsoft.com/en-us/library/system.security.cryptography.cryptostream.aspx

http://www.codeproject.com/Articles/6465/Using-CryptoStream-in-C

例:

byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8 }; // Where to store these keys is the tricky part, 
byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
string path = @"C:\path\to.file";

DESCryptoServiceProvider des = new DESCryptoServiceProvider();

// Encryption and serialization
using (var fStream = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var cryptoStream = new CryptoStream(fStream , des.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
    BinaryFormatter serializer = new BinaryFormatter();

    // This is where you serialize your data
    serializer.Serialize(cryptoStream, yourData);
}



// Decryption
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var cryptoStream = new CryptoStream(fs, des.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
    BinaryFormatter serializer = new BinaryFormatter();

    // Deserialize your data from file
    yourDataType yourData = (yourDataType)serializer.Deserialize(cryptoStream);
}
2
Fabjan

Resourcesに書き込むことはできません。詳細は this answer

実行時にリソース文字列を変更できない理由は、リソースが実行可能ファイルにコンパイルされるためです。コンパイル済みの* .exeまたは* .dllファイルをリバースエンジニアリングすると、実際にコード内の文字列を確認できます。既にコンパイルされた実行可能ファイルを編集することは決して良い考えではありません(ハックする場合を除きます)が、実行可能コードから実行しようとすると、実行中にファイルがロックされるため、まったく不可能です。

  • -を使用して、ファイルに読み取り専用またはHidden属性を追加できます File.SetAttributes 、ただし、ユーザーは引き続きウィンドウから属性を削除し、ファイルを編集できます。

例:

File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
  • 私が提案できる別の方法は、ユーザーが編集可能または重要なファイルと考えることができないように、奇妙な拡張子を持つファイルにデータを保存することです。 ghf.ytrなどの気まぐれは、今より気味の悪い気まぐれは考えられません!
  • また、.dll拡張子を持つテキストファイルを作成し、system32などのWindowsフォルダーの1つに保存することをお勧めします。この方法では、ユーザーはスコア情報がどこに行くのかを見つけるのに非常に苦労します!
1
Alex Jolig

ファイルにスコアテーブルが含まれていることを示唆しない名前(たとえば、YourApp.dat)としてファイルに名前を付けて、コンテンツを暗号化できます。

受け入れられる回答 here には、テキストの暗号化と復号化のコードが含まれています。

更新
暗号化のパスワードとしていくつかのGuidを使用することもお勧めします。

1
Dmitri Trofimov

テキストファイルを編集不可にするコードを次に示します。同様に、この手法を使用して読み取り不可能にするなど。

string pathfile = @"C:\Users\Public\Documents\Filepath.txt";

if (File.Exists(pathfile))
{
    File.Delete(pathfile);
}
if (!File.Exists(pathfile))
{
    using (FileStream fs = File.Create(pathfile))
    {
        Byte[] info = new UTF8Encoding(true).GetBytes("your text to be written to the file place here");

        FileSecurity fsec = File.GetAccessControl(pathfile);
        fsec.AddAccessRule(new FileSystemAccessRule("Everyone",
        FileSystemRights.WriteData, AccessControlType.Deny));
        File.SetAccessControl(pathfile, fsec);
    }
}
0