Should I avoid using BinaryFormatter altogether?


I’m just moving on from using Player Prefs and while reading about the next stage of saving data came across this:
The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they’re processing to be trustworthy. BinaryFormatter is insecure and can’t be made secure.

Should I completely avoid using BinaryFormatter? There is just so much much training material available on how to add this to apps/games. I’ll be using it to store save game data for an app with security being of some importance much further down the line. The way I see it is if someone really wants to cheat they will.

Any advice would be appreciated, especially on the alternatives.

BF will have inconsistent file reading and serializable class parsing, especially when updating your application and adding variable to the serialized classes.

From having used BF i recommend you switch to a json-type saving format. I add an encryption on top of it to avoid fraudulent file manipulation.

JSONUtility should give you enough flexibility (example below).

public static BonusSet LoadPlayerBonus()
    string file = EncryptDecrypt(System.IO.File.ReadAllText(path));
    BonusSet result = JsonUtility.FromJson<BonusSet>(file);
    return result;

EDIT : As you mentionned, someone who really wants to cheat will find a way to do so, either by manipulating files or modifying ram data. Having a server-side information management is usually the best, as it increases the work to cheat (small games are not usually hacking targets…). Make it a bit hard to hack and you should be fine. There is no magic solution unfortunately :-/

Yes, you should stop using the BinaryFormatter for deserializing external data. Note that security is not always about keeping your game data hidden but security in a way broader field. So using the BinaryFormatter can actually be a security risk for the user of your game. Admittedly the scenarios where this could actually be an issue are quite rare, but nevertheless it’s an unnecessary risk.

Apart from that storing data on the users side will never be secure. It’s actually the contrary. Since the format used by the BinaryFormatter is well documented and described, it’s easy to read - modify - write any data you stored there. I’ve actually written a parser for the remoting protocol (which is used by the BinaryFormatter) myself from scratch just using the documentation. Once you have that you could essentially create an editor for any kind of save file out there that is based on the BinaryFormatter.

If you want to make it harder for users to manipulate the save data there are many different approaches out there. Yes, you can simply use a human readable format like json and just add some scrambling to it to make it harder to read. Though in the end no amount of encryption done on the client side will be enough to make a save file “secure” since the client itself has to be able to read it, so all encryption keys you may have used has to be available to the client. IL2CPP will make it harder to simply decompile / view your code, however I’ve seen decompilers out there which get close to normal .NET decompilation. Even if you outsource the encryption to a native code library, a cheater can simply use your library or do a more in depth analysis of the assembly. Once it’s known what you did and how, the data is not secure anymore (which means it never was in the first place). Security through obscurity is not security at all ^^.

Just rolling your own binary format is just a little bit more code you have to write, but on the flipside you don’t really need to create seperate classes which are serialized since you can decide yourself on the fly what to serialize and how. The resulting save file would also be much smaller than what the BinaryFormatter produces. .NET comes with two classes: BinaryReader and BinaryWriter which lets you directly serialize primitive values into a data stream. Though when you roll your own format, you should always start with a version ID and always check the version when deserializing. Some sort for version info should also be included when you use a json based format because maybe in the future you will have breaking changes to the format and you have to be able to distinguish old from new files.

BinaryFormatter is a bad idea, even in a small single-player game.

Cheating isn’t the issue. The issue is providing a vector for bad actors to execute code on your player’s system. The point of the warning on Microsoft’s site is that deserializing data with it causes code execution. It would be entirely possible to distribute a “save game” file for your game that, when loaded by an unsuspecting player, downloads and installs malware on their machine.

Encryption in such a scenario is meaningless - your game must already contain everything needed to create or decrypt such a file already and a determined attacker would eventually find what they need to create a malicious save file.