# Validating victory report to PHP leaderboards

I have decided to begin working on some sort of leaderboard server for my PC game.

For starters, I decided to write some PHP scripts to handle it. The script doesn't actually receive a score - it just receives a player ID. Then the server increases their score. When submitting a "victory", the game performs a connection to the server of this form:

http://www.example.com/doVictory.php?ID=Joe


As you can see, the server just receives the player ID and then it does its own scoring stuff (the score value of a victory changes overtime).

My concern is pretty simple: what if some guy figures this out? They would be able to spam the above address several times and keep increasing their score.

One option:

• Don't accept victories from the same player within a short amount of time.
• Due to the nature of the game, players can have a victory as fast as within 1 minute. If somebody is having a victory every minute, I would not be able to tell whether they are cheating or just very good.

I have found an answer here: https://gamedev.stackexchange.com/a/4185/4779

• Send a JSON message over plain HTTP (not HTTPS). Include a MD5-hash of all fields plus a magic string.
• On the server, check the integrity of the message with the same operation.

But as far as I'm concerned, this doesn't quite work with my scenario because I am not really sending a score - just reporting a victory.

What you will need to do is replay the entire game on your sever. If you use randomness in your game then store for each player the random seed you send them on your server and then have the server play the game to validate that their moves do indeed lead to a victory. This has the advantage that other types of cheating won't work either. If it's multi-player you can also ask the loser to send a message as well and then make a sanity check to see if the same player keeps losing against the same opponent.

Now let's for example look at a game of chess: As you might know it's easy to represent chess games as strings. If you receive such a string it's fairly trivial to detect any illegal moves and games that didn't end in a win. So you can at least check for a valid game now. If you play against an AI you can also look at what seed the AI used while playing (send this to him from the server and store it or else a player can keep playing against the same AI by messing with the seed->keep sending the same message without actually playing). You can now check if the moves the AI made check against the behaviour you would expect it to have given it's current state. If you are smart you even can even tinker with the random generator to be based entirely on the original seed and the moves made so far as this means that you will not have to validate every AI move but can just sample a few of them (and if a player repeatedly sends bad AI moves you can simply ban him).

If you play against another player you can also let him validate the game and check to see if a given player/IP-adress is losing far more then they should.

Another option is to hash the information. Send the player ID, the date, and a hashed string representing the data. You can encrypt a bit of data in the string to make it uncrackable.

eg.

Name = Joe
Timestamp = 2015-01-15 13:23:44


Then you can concatenate into a string, and add a hash.

(e.g. using MD5)..

Joe,2015-01-15 13:23:44,uniquecode
=
998156c21a891abcbee2afd1988d6a0c


Then send this data to your server...

http://www.example.com/doVictory.php?ID=Joe&Timestamp=2015-01-15%2013%3A23%3A44&Hash=998156c21a891abcbee2afd1988d6a0c


You can then recreate the string on the server from the 'ID' and 'Timestamp' parameters, and your 'secret key'. You can then recreate the hash, and check if it's the same as the one supplied. Providing the timestamp also means you can check when the information was sent, and act accordingly.

This way, you can choose to add to the score if the timestamp is within the last minute. The hash should also be un-recreatable as the user doesn't know your secret key.

Just change the method, from GET to POST. That will send the needed information but it will not be visible in the link.

• The data still exists in the request and is trivially spoofable. The OP requires ways of avoiding replay attacks, for example by means of incorporating unique tokens in the reported game state that cannot be reused. Obfuscation does not gain you any measurable security. – Lars Viklund Jan 19 '15 at 10:30