- Home /
Should I avoid using BinaryFormatter altogether?
Hi,
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.
https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide
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.
Answer by Spip5 · Apr 11, 2021 at 01:11 PM
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 :-/
Thank you, from all of the research I've completed json with a bit of encryption is what seems to hit the sweet spot between easy but accessible vs hard and being a hassle.
Cheers
Answer by Bunny83 · Apr 11, 2021 at 04:33 PM
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.
I disagree, while one can argue that anything that is not encrypted is open to fraudulent activity, the key here is encryption, as well as validation of your objects as a secondary measure.
I think I don't really understand what you want to say here. The BF does not apply any encryption and why is that relevant? The OP doesn't actually state why the BF was used. I just added some additional information about encryption and security through obscurity.
Encryption only works when the key is secret. However if the app needs to decrypt the data on the client side, the client has to have access to the key. So no matter how advanced the encryption is, when you have access to the key it can be decrypted / manipulated.
So what exactly are you disagreeing with? Are you sure you read my whole answer and you understood the question?
Because one has to decrypt it, to know what it is, to begin with! Plenty of ways to make the private key private in Unity.
CyberAngel is correct you can add encryption when using a binary formatter. I have used it in the past using Sha256.
I think you didn't get the point. You can add an encryption layer around any kind of encoding, no matter if it's JSON, XML or a binary format. The points were that first, the BinaryFormatter itself does not really add any kind of security on its own but many argue that they use the BF because it's "more secure" which is just not true.
In my answer I said that you can add any kind of encryption on top to make it "a bit harder". However you do not add any security by adding an encryption layer since you have to ship the key with the data. Security can only be achieved when you can keep the key secret. Since the client has to be able to decrypt the data, it has to have the key. Therefore it's just security through obscurity which is no security at all. It's just "annoying" for an attacker, but not security.
The thing to note is that you can do private and public, and IL2CPP. So even if they can first get to your source code, they then need to know what the private key is, and that is not going to be easy.
So to create a payload in the first place is going to have to get past what type of save system you have in place, from there, they then need to know how to decrypt and then they need to work out how to create a payload with the right private encryption to deliver the payload.
Now I am not saying it is impossible, but I think until there is a safer way to serialize objects, people need to be educated that you can secure things.
I mean, we send sql parameters over the wire all the time, we are taught to encrypt that, so why don't we stop using SQL parameters over the wire?
Apert from that sha256 is not an encryption at all. It's just a hash function. Hash functions are not encryption as it's a one way function. It can't be reversed. So this has nothing to do with the topic here.
Thanks for your reply here. I really glad to know about it.
Answer by broberson · Jan 20 at 07:33 AM
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.
Don't agree, because if they haven't got the key to encode it, they aren't going to be able to deliver a payload to execute on your machine.
Granted it is not 100% perfect, but I think someone has more serious issues if they are downloading save files from unknown sources!
I am not quite sure what you mean. The problem as i understood is that if a user takes a malicious save file from somewhere then it can execute malicious data. So what exactly do you mean by encoding and decoding? You would have to use for example a cryptostream for this and use a key that is unique per distribution. However microsoft says there is no way to make deserialization secure.
That's correct, you can't.
But what you can do, is make it obscure enough that someone doesn't know what the file is. So if people share their Save Files, and/or grab save files from the net, then this opens them up to these sort of issues.
The number one thing, is knowing where that file came from, can it be trusted. The only way that can happen is you don't drop unknow save files into your games. Aside from that, if it is secured via encryption you can use ones with private and public keys, and it would make it extremely difficult for someone to decrypt it and re-encrypt it with a payload.
But for FULL piece of mind, you can avoid BinaryFormatter altogether, if you so desire.