49

TL;DR

I am working on a gaming system that uses UnityScript and C# on the client and PHP on the server. A MD5 hash of the data plus a shared secret is used to check that the data has not been modified in transit. Is MD5 good enough for this? What other hash algorithm could I use that works in all three languages?

The Problem In More Detail

I have come across some code on a widely used community website about the popular Game Development Platform Unity, and I am now working on improving the MySQL, PHP and security of that code.

The code uses a "secret key" value that is shared between the client and the server. All messages from the client includes a hash of the data (e.g. name and score) plus the secret key, that the server checks before accepting the data. This is basically an authentication that the data passed has not been tampered with.

However, because it's MD5 I think someone who is listening to the network traffic could easily work out the secret key and then post whatever data they want to the server.

So my questions are:

  • Does this current state of affairs warrent improvement? Or is this the intended current use of MD5 (as of January 2017)?
  • Is there another hashing algorithm that could further improve/authenticate this communication activity? Please note that the algorithm would need to work in PHP, UnityScript and C#.

The Code

  • In UnityScript (client side):

    var hash=Md5.Md5Sum(name + score + secretKey);
    
  • In C# (client side):

    string hash = MD5Test.Md5Sum(name + score + secretKey);
    
  • In PHP (server side):

    $secretKey = "mySecretKey"; // Change this value to match the value 
                                //stored in the client javascript below 
    
    $realHash = md5($_GET['name'] . $_GET['score'] . $secretKey); 
    if($realHash == $hash) {
           //interact with database 
    }
    

Further Criteria

  • Some Unity programmers use UnityScript (a Python-like language with a JavaScript-like syntax on the .NET API) but no library can be assumed to be installed.

  • Game Developers are not PHP / MySQL programmers so complex or 3rd party PHP/C#/Js code would probably not be helpful.

  • BCrypt is apparently unwise to use with C#

  • BCrypt needs a static salt in this instance (perhaps derived from the secret key?) but is intended to work with a random salt.

  • PBKDF2 seems to be prefered over BCrypt but can be very slow particularly on mobile devices without much memory.

  • Dealing with a secured server can not be expected (if only...).

  • I don't know enough about the C# security library to really pick out best options from those listed.

  • While the code outlined is simply to do with highscore updates, this code has been in the past - and will be in the future - taken and used for transporting all sorts of data, public and private to various databases.

  • Dealing with hash algorithm interoperability between PHP, UnityScript, C# is a bigger hurdle than I had anticipated. If it was just PHP I'd use password_hash.

Some Thoughts and background to this question:

I updated the title as the edited title seemed to suggest that I wasn't sure about changing MD5, whereas knowing I should change MD5 was one of the core reasons of asking the question here in the first place.

The original question was that I wanted to update the terrible code suggestions given on (amongst other places) here about how to handle interactions between a game on a client machine and data storage on a remote server. Bare in mind this is code suggestions for beginner programmers in Unity and this site is [now] run by Unity Technologies themselves.
If you look, the original question (linked above) was using PHP Mysql_ functions (as well as a rather crappy invalid form of PDO). I felt this would benefit from a rewrite. I saw that the original code had also used an md5 routine to hash the intended data. When it came to the replacement of MD5, I hadn't realised either the vulnerability of compiled project files or the size/scale of the work needed to make this codeblock be actually more secure (on either the interaction with the server or the client side data). My original quest was to find a suitable drop in replacement for the MD5 which could work in the varous languages required (UnityScript, C#, PHP) , as I was aware of it's shortfalls. I hadn't realised (judging by the comments here) how tediously easy it actually is to break into exe's and grab hardcoded data.

This question is NOT about a game I'm making, it is not about My project and the code quoted that I am intending to replace was not written by me. I read a lot of the comments that somehow people are having a go at the messenger, but this question came from my own wish to improve an existing shortcoming on a teaching wiki website. I do have the greatest respect for the knowledge shared in answering this question but I am aware that from the last 6 months exploring the Unity documentation and learning sites that there is a significant gap in securing both local applications and multiplayer or other remote interactions.

I see a lot of responders in comments stating that the answer given by George is a bad answer - but it answers the specific question I asked, at the time. Thanks.

Final Note:
This Blob post from comments by Luke Briggs underlined how much of an eye openingly easy process it is to manipulate local Unity Game Application data. I did not at all comprehend how vulnerable local files are....

7
  • 1
    Comments are not for extended discussion; this conversation has been moved to chat.
    – Rory Alsop
    Commented Jan 3, 2017 at 11:25
  • Now, please stop leaving comments here - leave them in the chat as required. And moe here will be deleted.
    – Rory Alsop
    Commented Jan 3, 2017 at 21:24
  • 1
    It's not terribly clear from the question, but are you using Unity? It needs to be noted that Unity does not support JavaScript. Rather, it supports UnityScript (a Python-derived language with a C/JS-like syntax on .NET), which is falsely advertised by Unity Technologies as “JavaScript”. This makes a significant difference for how to answer your Q because JS's standard libraries are not available in UnityScript. Commented Jan 4, 2017 at 18:56
  • writing in Unity I use C# and know the "javascript" and "Boo" languages by reference only, in the context of Unity. @SlippD.Thompson
    – Martin
    Commented Jan 4, 2017 at 19:05
  • 2
    What you're implementing is a (H)MAC, you might want to have a look at some more info regarding that -> en.wikipedia.org/wiki/Hash-based_message_authentication_code In 2011 an informational RFC 6151 was approved to update the security considerations in MD5 and HMAC-MD5. For HMAC-MD5 the RFC summarizes that - although the security of the MD5 hash function itself is severely compromised - the currently known " attacks on HMAC-MD5 do not seem to indicate a practical vulnerability when used as a message authentication code."
    – BlueCacti
    Commented Jan 6, 2017 at 12:08

4 Answers 4

27

Edit: To clarify, I can't speak to the security of your approach, only the hash portion. There are some strong comments here discouraging your approach. (i.e. a good hash can be part of a secure approach or an insecure approach, just like a good lock can be used on either a good door/frame, or a flimsy door/frame)

In response to your TL;DR I can say quite assuredly that SHA-2 is the recommended drop-in replacement hash function.

If length is an issue, it is better to use a truncated SHA-2 than MD5.

SHA-2 comes in 4 sizes: 224, 256, 384, or 512; with 256 and 512 being the most common.

Keep in mind, this is a General Purpose (Fast) hash, and is not suitable for password storage, however it is suitable to ensure data integrity as your TL;DR suggests.

BCrypt is a Slow Password Hash and is used if you need a sort of one way encryption. (i.e. the password is never stored, only its hash, and is difficult to brute force)

19
  • 12
    While this is true, it's worth noting the caveat that none of these are sensible authentication options by comparison to proper SRP schemes, and won't help with the actual problem OP is facing (users faking values).
    – Polynomial
    Commented Jan 2, 2017 at 22:28
  • 23
    @Martin SHA2 family would be the ideal replacement for MD5 in almost any case you come across MD5. That doesn't change the fact that your approach is broken, and changing the hash does nothing to improve security, but MD5 is indeed deprecated and SHA2 family hashes are indeed the recommended replacement for most use-cases not including hashing of passwords.
    – Polynomial
    Commented Jan 2, 2017 at 22:29
  • 37
    @Martin As a Unity developer who sells on the Asset Store I sincerely hope you won't use this answer. Listen to Polynomial. Many Unity developers do understand how cheating works; your scheme is completely flawed - teaching it/ selling it to other people will spread misinformation. Commented Jan 3, 2017 at 4:47
  • 6
    @Martin for your reference I've just pulled together two simple images. See this and also this. Games are different from the web because it's the client who is trying to exploit your system (in order to cheat). Unlike on the web, where it's typically a 3rd party trying to exploit the client (in order to steal from them). Commented Jan 3, 2017 at 6:23
  • 6
    I feel that I have to downvote this on the basis that it is completely off topic. The hashing is done on the client side, so it's utterly trivial for anyone with technical skill to modify the values that are hashed. The "secret key" cannot possibly be kept secret because it's given to the client. The whole hashing stuff in the post is really quite nonsensical. @Polynomial's answer much better illustrates how the OP is approaching the problem in a very flawed way and that hashing here just doesn't make sense. Because it doesn't make sense, there's no reason for elaboration.
    – Kat
    Commented Jan 4, 2017 at 1:30
202

This approach is fundamentally flawed. Anything on the client side can and will be tampered with by players. It is the same problem which makes DRM untenable - the user owns the machine and all the data on it, including your executables, data in memory, etc. Keeping algorithms secret doesn't work (see Kerckhoffs's principle) because it only takes a small amount of reverse engineering work to work out what your code is doing.

For example, let's say you've got a routine in your game client which posts the level score up to the server, using some cryptography of whatever form to ensure that it isn't tampered with over the network. There are a whole bunch of ways to get around this:

  • Use a memory editing tool such as Cheat Engine to scan for the current score (pause, search the score, unpause, wait for score to change, search again, repeat until you find the memory address which contains the value) and edit it. When the level completes the score value will be happily treated as legitimate by your code, and uploaded to the server.
  • Modify the game executable on disk so that your "level complete" code ignores the real score value and picks a different one.
  • Modify the game executable on disk so that simple things (e.g. killing one monster) increases your score by 1000x more than it should do.
  • Modify the game executable on disk so that you can never die, or have infinite powerups, or one-hit kills, or any other number of helping things, so that you can easily attain a very high score.
  • Perform any of those modifications in-memory after the game loads so that the original executable stays intact (useful for cases where there are annoying integrity checks)
  • Simply expose the "we finished a level, now upload the score" code externally from the process so that it can be called by anyone's program. In native code you can inject a small stub and add an entry to the export table, or just directly copy the code into your own executable. In .NET it's trivial to just modify the class and method's visibility flags and import it into a new program. Now you can submit whatever score values you like without ever even running the game.
  • Reverse engineer the game and get hold of the "secret" key and write your own app to send the score value to the server.

This is just the tip of the iceberg. For more complex games there are all sorts of workarounds to anti-cheat and other problems, but regardless of the defense tricks used there will always be a way to mess with client-side values.

The critical feature of a secure approach is that nothing on the player's computer should be trusted. When your server code receives a packet from a player, assume that your game might not even be running - it could be a totally homebrew piece of code that lies about everything.

Securing multiplayer games (or any kind of game where verifying game state is a requirement) isn't easy. The problem isn't really even a security one, it's a usability and performance one. The simplest way to secure a multiplayer game is to keep the entire game state on the server side, have all the game logic executed and maintained there, and have the client do nothing but send player input over to the server ("user clicked the mouse, user is holding W key") and present the audio and video back to the player. The problem with doing this is that it doesn't make for a very fun game due to network latency, and it's quite hard to scale on the server side. Instead, you have to find a balance between keeping things client-side and server-side. For example, the logic for "does the player have a key to open this door?" must be checked server side, but the logic of when to show the context icon for "open door" stays on the client side. Again, things like enemy health and position must be kept on the server side, and the player's ability to deal damage to that enemy must also be verified (is it near enough?) which means that AI can't be kept client-side, but things like the choice of which idle animation the enemy is displaying will probably be a client-side thing.

We have a few questions on here about game security, so I suggest reading through those:

I also recommend these questions over at GameDev SE:

There's also a great paper on multiplayer security from BlackHat EU 2013, and an article on non-authoritative P2P networking, which may both be of use.

9
  • 22
    It's a simple matter of economics. Until recently, there wasn't much money to be made by cheating in games. Or, to look at it from the other side: there wasn't much money to be lost by being cheated, so why bother spending money to prevent cheating? The same is not true of the web, ever since eCommerce, and then eBanking became a thing. Commented Jan 3, 2017 at 0:21
  • 11
    @JörgWMittag It's not even that. People are just generally more acquainted with how to cheat in multiplayer games these days, and the tools available make things so much easier. The financial economics aspect still matters in some areas (particularly MMO bots), but more than anything it's the barrier to entry that was lowered. These days you can make a standalone game trainer that does code patching and memory editing without ever writing a single line of code yourself, or even really understanding the underlying concepts of what you're doing.
    – Polynomial
    Commented Jan 3, 2017 at 0:40
  • 1
    Heck, games used to include cheat codes, so exploiting wasn't necessary. It wasn't until people were competing over the Internet that anyone cared about preventing cheating. Commented Jan 3, 2017 at 15:54
  • 2
    I was about to post a comment in the answer but luckily checked other answers. THIS is the answer that should be marked as the correct one. The only way to ensure the player has not cheated is for the game to send the input sequence to the server and the server to replay it back - it's done this way in DROD, demo consists of player moves, the demo is sent to the server as a score and only when Spider plays it back and confirms the end state is exactly the same it is marked as a valid score.
    – Maurycy
    Commented Jan 3, 2017 at 17:31
  • 11
    When your server code receives a packet from a player, assume that your game might not even be running - it could be a totally homebrew piece of code that lies about everything. So true. When we had dial-up internet we'd call the number by hand and make modem noises (the geek version of beatboxing, I guess). While we didn't know what was going on, we typically added several seconds to the time before the remote side hung up. I always wondered if someone could spend enough time that they'd be able to play a game remotely or something, with just their voice. Commented Jan 3, 2017 at 19:55
40

There are three problems here:

  • What you're trying to do is fundamentally misguided in several ways, as described in Polynomial's answer.

  • If you insist on doing it anyway, then you're not even using the right primitive! Authenticating messages requires a MAC, not a hash. It's possible to build a MAC out of a hash -- this is what HMAC does -- but it's not as trivial as it looks. What the code you pasted is doing is trying to naively build a MAC out of a hash, and these kinds of naive constructions are usually broken.

  • Also yeah MD5 is a bad hash function. But this is really the least of your worries... MD5 is not the weak point of this design. You're asking for advice on how to replace the bars over your windows with 2-inch thick steel plating -- but your front door is wide open. Justifying this as being "better security" makes no sense.

0
11

A friend of mine once made a flash game (yes it's that long ago) and used the same scheme: md5(authenticated data + secret key), then validate it on the server.

I reverse engineered it with some tool and found the secret value he was using in about 30 minutes. Game over.

But yeah, it's the second-best possible option. Regardless of using sha2 instead of md5 and regardless of using hmac instead of a hashing algorithm, user input is never to be trusted. If users run your game on their machine (i.e. it's under their control) and if they can submit a high score, they will always be able to use tools to enhance the scores.

The best thing you can do is to upload a replay (e.g. recorded keypresses) and re-run it on the server, then validate the resulting time. This is a lot of work to code up, takes more server resources, and you will need to have deterministic physics if you use floats anywhere... but that's the only thing better than what you're doing, and people can still script the game if they want to cheat.

1
  • 2
    For turn-based games which allow repeated plays with the same seed, and which are complicated enough that developing an algorithm to find good solutions would itself be an accomplishment, I think one could render the concept of "cheat" moot. For action games, however, there would be no practical way to determine whether someone actually played the game in real time.
    – supercat
    Commented Jan 4, 2017 at 0:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .