MD5 hashing

Started by
7 comments, last by Antheus 16 years, 1 month ago
Hello all, I had come up with a scheme for minimizing the number of hacked files in my multiplayer game. The scheme is basically as follows:
upon client connection to server
   for each media file 'x' in server list
      server requests md5 hash from client for 'x'
      if client has 'x'
         calculate md5 hash
         send to server
      otherwise
         server sends download location for new file 'x'
         client downloads new copy of 'x'
once all files' md5 hash is validated with server, set client in 'play game' state

The pros of this system are: - clients never need to know what media files are required by the server (nor where master copies are kept), only IF their own copies need to be updated - any files modified by clients (e.g. adding alpha to wall textures) are overwritten each game launch, before they can be useful - server can update files at will without needing to issue specific patches - only client and supporting DLLs need to be distributed for initial installs, media files can be downloaded at launch The cons are: - md5 hashing may take time on slower clients - redownloading files can take time - doesn't protect against memory hacks after files are loaded I had also considered coupling this with passworded compressed files, for added media-file integrity. Is this a good idea? Terrible idea? What other methods could I use to prevent user-editing of media files? Best regards,
Advertisement
"server requests md5 hash from client for 'x'"

there's your problem. You trusted the client. It can be very easy for hackers to change the directory you are reading the files from and swap it back when needed. Or send you what you want to see.

I send files XORed through PHP. So when people access images from my content server they get lightly encrypted then just decrypted by the client. Keeps newbs from getting the files because it requires a simple decryption of the flash client.

Don't waste your time though trying to protect things that the client has 100% access to.
As you might know, remotely making sure the client is in a given state is theoretically impossible. Having that in mind, the next best thing is to make sure changing that state is at least hard. And I don't think your approach is hard enough.

All the client as to do is pass that test once, with a "clean" client, record the hashes the server expects to receive, and the next time he connects he can make whatever changes to the files he wishes, all he as to do is resend the expected hashes to the server again.

This happens because the server is expecting constant values, what I'd suggest you do is have the server send the client a random value, that will influence the answer the client will later send the server.

For instance:
Have the server send a random 32 bit number to the client, and have the client xor that number with each 32 bits of each file before applying the hash function.

This approach isn't foolproof either.
The client can always change the files after the check is performed.
You can send successive checks to the client at random times to make sure the files are still intact, but again the client can just analyze the traffic and switch the files at the right times.

Quote:Original post by Sirisian
Don't waste your time though trying to protect things that the client has 100% access to.

Take Sirisian's advice. =)
Quote:remotely making sure the client is in a given state is theoretically impossible


Actually, it's not 100% impossible. It's simply infeasible in most current deployment conditions.

If you have a trusted hardware platform (a la Palladium, etc), then you can ensure that nothing has been compromised.

Also, if you have a trusted machine on the same physical subnet as the checked machine, there are certain things you can do with disabling all interrupts and timing the calculation time for certain hash functions. You would need very accurate timing measurements on the trusted machine, though, and this only works if the untrusted machine is the fastest possible machine (else it could delegate to a faster machine, and still cheat).

That being said, you all are giving good advice:
- Do the very simple thing to keep honest people honest.
- Make sure the system is self-healing to take care of bona fide corruption.
- Fight hackers at the social systems, game design and server authority level.
enum Bool { True, False, FileNotFound };
Yeah, and above all NEVER trust the client. You must ALWAYS assume that the client is wrong and needs to be corrected in order to maintain tight security.

The dev project that I'm part of first looked at the method you originally posted. We thought that maybe we could just collect MD5's of all of the files to make sure the client is valid. After much deliberation we came to the same conclusion the above posters did:
Quote:Original post by Sirisian
Don't waste your time though trying to protect things that the client has 100% access to.


As such we had to look for other ways of verifying the authenticity of the client. One such method that we are still working on resembles what Xor posted. First, the checks are performed at random intervals reducing the chances that the client can "fool" the server. Second, the values of the checks use a custom hashing method (based on MD5) that is seeded with a random number from the server that way the hash cannot be faked since the method is custom.

Another method which we are still debating is the possibility of hiding the "checks" inside other packets such as stats updates or movement updates. Each packet will contain one check from one file. The "check" may even be a false one to throw off packet hacks. As the data gets sent, the server is able to eventually verify all files. The file selection will be random based on a seed from the server. With this setup, the client will have a VERY difficult time determining what file is going to be checked if any.
It's not enough just to program, you have to put your heart in soul into every byte of source in order to show your worth.
Do what GW does.

Let client maintain update count. Each time you update resources on server, increment it.

When client connects, it sends its own update count, server sends all resources that were update since that point.

This involves sending a single 32-bit number. If client manipulates it, two things can happen:
- Setting it to earlier value means they'll need to re-download content they already have. Their problem.
- Setting to arbitrarily high number - they'll receive all new content, after the update is complete, client will set it to latest update count.

Then, you can build a bridge system that uses version management software to generate delta lists between versions. This has additional management benefit that your server-side resources are tracked as part of production pipeline. In addition, a single number identifies all the changes that happen to client-side repository - not only additions, but also updates and removals.

And - developers do not need to do anything extra, drastically reducing overhead.

In addition, since it's version repository based, you can always run tests or make test deployments for any version. Should bugs pop up in previous version, just check out everything from repository and test it.

Client may mess with this number, but it won't do them any good. They'll either receive all resources, none, or redownload those they already have.

Optionally, add a repair option, where client sends individual file hashes and server checks them. This is slow since it may require sending several 100k of hash numbers.

At worst, if malicious client sends fake hashes for altered content, they'll be playing game with out-of-sync resources, and things won't work for *them*, but server will be rejecting their commands.

PS. Hashing is very slow. Avoid it as much as possible. Many users have slow disks, so even doing simple CRC can take a long time, possibly several minutes or more.
I don't see how that would work out at all. The client could very easily set the counter manually after they've loaded their modified client data. From there is open sailing for them to falsify the information they are sending out to the server.

You also say that the client would be just fine with modified content and that the server would reject the client commands. First of all, how in the world could the server reject the client commands if the server thinks that all is well with the client since the client sent a valid counter / hash list? Do you have some way of somehow verifying the client authenticity without any data from the client? You're assuming that the client is right. Like I said before, always assume the client is wrong and needs to be corrected.

Plus, using the method I posted earlier, the hashing can be done in a thread separate from the main game thread(s). That way you don't notice it. As such, any hashing method can be used since its run in the background.

Sure my method is more complicated and uses more overhead, but it's MUCH more secure and all in all it's not that hard to implement. I started on a prototype implementation of it just today in fact and I haven't had to add much more code. The only hitch I've run into is a way to get a minimalistic hash but still be reliable. MD5 is WAY to big to send in every packet. That's an additional 32 bytes (in string format) on every packet (which is alot considering most of the packets are ~60 bytes).

But all in all, a well written server won't even need all of this. If your server is running the game-state and can check for illegal information real-time then all of this is unneeded. The data the server handles can easily be monitored and corrected. However, server-side checks for positioning is the hardest one of all since the server needs to know the position of the terrain and other static geometry and it needs to be able to perform collision detections. But I digress.

In conclusion, there are several ways of maintaining a valid client. These range from completely and totally insecure to Fort Knox, lock-down type setups. Your pick which one is right for you.
It's not enough just to program, you have to put your heart in soul into every byte of source in order to show your worth.
Theres also some evil optimizations you can do for alot of this stuff. Like you mentioned movement. A great one is to have the client check movement, if the movement is blocked no problem we are happy. If the movement is allowed, its off to server verification land.

This same scheme can be carried out in a lot of logic, have the client perform what is considered safe and not validate it, and anything that is viewed as unsafe you validate. This can severly reduce bandwith needs.
Quote:don't see how that would work out at all. The client could very easily set the counter manually after they've loaded their modified client data. From there is open sailing for them to falsify the information they are sending out to the server.


It works for Anet and Guild Wars. Read the design as I described it again.

There are three known attack vectors in GW.
- Client-side analog hole, which modifies textures as they are sent to the graphic card. Harmless, falls under modding category.
- In memory client monitoring (issuing keypresses, monitoring for certain parameters), used by bots.
- Third-party account attacks, such as trojans or phishing on fan sites. Not part of game.

If client modifies the map, it doesn't matter. Movement and all actions are completely server-side validated. If they removed client-side collision maps it wouldn't matter. They'd be running forward, but everyone else would see them stuck up against some wall.

Skills are timed server-side. Client can change recharge rates to 0 seconds, server would merely detect that and disconnect the client, or refuse to execute the skill.

Everything else is completely unrelated to client-side files or even client. All other attacks cannot be prevent through checksumming of any kind.

Quote:but it's MUCH more secure


No it's not. You're asking client to send hashes. You've gained nothing whatsoever, since hashes can be spoofed trivially.

This topic is closed to new replies.

Advertisement