47
\$\begingroup\$

Browser and mobile games commonly have global highscore tables. It's also common for those tables to contain scores of 2,147,483,647 — where people have figured out the webservice call that reports scores and used it to record a fictitious score.

For simple puzzle games, we can defend against this by including a record of every move the player made (and any random seeds used to generate the level) with the score-reporting call. The entire game can then be reproduced and verified on the server.

However, this quickly becomes infeasible for anything larger than Pac-man.

How else can cheating of this kind be prevented?

\$\endgroup\$
3
  • \$\begingroup\$ I had this exact same question regarding iPhone games using the same technique to create a global highscore table. \$\endgroup\$
    – deft_code
    Sep 30, 2010 at 14:53
  • \$\begingroup\$ Are you sure it would be infeasible to send the replay? \$\endgroup\$
    – o0'.
    Feb 15, 2012 at 8:34
  • \$\begingroup\$ Yes I am sure. :) \$\endgroup\$
    – teedyay
    Feb 15, 2012 at 10:09

10 Answers 10

10
\$\begingroup\$

The internal system we used for Moblox (later replaced with OpenFeint) worked like this:

  • 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.

To crack the system, you'd have to find this magic string. It is possible with reverse engineering, but painful.

OpenFeint, ScoreLoop and CocosLive all use the same trick, but with HTTPS. Very easy to implement.

\$\endgroup\$
6
  • 29
    \$\begingroup\$ I'm quite dubious that reverse engineering to find your magic string would be all that difficult. \$\endgroup\$
    – Kylotan
    Oct 1, 2010 at 10:46
  • 4
    \$\begingroup\$ This is an Android Application in native C++. There is no symbol, nor good debugger available. So you can read the ARM code, but not trace it easily. The key is composed by multiple operations so find all strings is not sufficient. That is not perfect but quite painful. \$\endgroup\$
    – Ellis
    Oct 1, 2010 at 18:18
  • 3
    \$\begingroup\$ A standard undergraduate exercise, at least in US universities, is to reverse-engineer passwords constructed as you describe, by reading y86 (cgi2.cs.rpi.edu/~hollingd/comporg-spring2007/notes/Y86/…) assembly code. \$\endgroup\$
    – user744
    Oct 6, 2010 at 12:49
  • 14
    \$\begingroup\$ This is a terribly weak technique, which will give to the developers a very false sense of security. \$\endgroup\$
    – o0'.
    Feb 15, 2012 at 8:33
  • 2
    \$\begingroup\$ This is a very good example of en.wikipedia.org/wiki/Security_through_obscurity \$\endgroup\$
    – kaoD
    Feb 15, 2012 at 11:55
21
\$\begingroup\$

While you're right that it's not always feasible to send entire replays to the server for complex games, a similar system can be used by having the server periodically (and semi-randomly) ask the client for some part of its state, while the game is running.

For example, in an FPS, every minute you could ask "How many kills do you have?", "Where are all the enemies?", etc. If the client doesn't come back with a reasonable answer to a challenge in a reasonable amount of time, they are cheating.

Of course, this only works if the game is online during the entire play session. Since the goal here is to be able to upload to an online leaderboard, I think that's reasonable - don't kick the player out of the game if they answer wrong, just don't let them on the score list.

I would encourage you to reconsider sending replays however. All you really need is the initial random seed and timestamped input. This should really be a few hundred KB at most. Many arcade games do this already to save replays for player review purposes; having your server validate these might not be trivial, but it prevents all kinds of cheating except botting.

\$\endgroup\$
10
\$\begingroup\$

You can limit the most flagrant of abuses by monitoring the highest results in the top score table. Depending on your game, you may have a "perfect score," above which any score must be fraudulent. If not, you can calculate the lowest "impossible score;" can the player shoot 10 shots per second, the game lasts 1 minute, and each killed enemy is worth 100 points? Then any score higher than 60,000 must be fraudulent.

You can also help mitigate the problem by sending along some metadata; not the full game history, as you describe, but just the components that make up the score. Say: score 60000, 500 enemies killed, and one bonus item grabbed. You can then perform simple checks. This is "security through obscurity," and therefore not secure at all, but it helps eliminate the most naive of attackers.

\$\endgroup\$
3
  • \$\begingroup\$ You can also flag a user (possibly by IP) if they submit a score that doesn't match the metadata. Then if they try and submit a score again that DOES have correct metadata, you can scrutinise it, and possibly just ban them altogether. You could also send a cheeky message back from there submit score request :) \$\endgroup\$
    – Adam Harte
    Oct 3, 2010 at 19:27
  • \$\begingroup\$ I think this answer is better than the accepted answer. Just figure out which number is the maximum your players can achieve. Anything higher, just discard. \$\endgroup\$
    – Sergio
    Oct 5, 2010 at 16:16
  • 6
    \$\begingroup\$ So if one player who goes to a university cheats, everyone on the network is now a "cheater" \$\endgroup\$ Oct 5, 2010 at 20:54
6
\$\begingroup\$

Ultimately you can only rule out impossibly high scores, because the rest are (by definition) merely implausible, and thus might be a legitimate (and awesome) player.

Otherwise you have to rely on obfuscation techniques (such as encryption, and sending other stats beyond just score).

You could also send the score periodically as the game is being played, which would add another level of complexity to cheating - i.e. the server can decide if the game has been played long enough to warrant a particular score, and also ensure that enough intermediate reports were received during the play-time (just don't make it 100% or the train going into a tunnel on my way home will result in my throwing the phone out the window).

Ultimately someone will find a way to break it though, so don't kill yourself trying to stop them.

\$\endgroup\$
6
\$\begingroup\$

I added a quick/dirty high score table to a project of mine a while back and not being at all versed in internet security/etc it turned out sort of flawed. Surprisingly, with nearly 1,200,000 recorded scores, I've only had maybe 5 or 6 occasions of batches of blatantly incorrect scores reaching the top of the board. Most of the scores even looked more like a glitch in the game, than a true "hacking".

So I guess an important point is: make sure your game's scoring system is airtight, or at least do some really good score feasibility checking; now, this game I'm talking about was a Ludum Dare 48hr entry, so it wasn't the most stable thing around.. but on the whole I think it's often more likely that the casual player will discover/exploit an ingame glitch than having someone directly "hack" the leaderboard.

That said, I'm working on a rewrite of this project right now, and I'm going all out with the obfuscation. I won't go into too much detail, but I basically have all scores submit a key value based on a bunch of random values and hashing and a magic string, then any score that passes that check and is high enough to make the actual "Top X" leaderboard has to pass another round of validation (this time with an expiring key value generated on the server end, and more thorough feasibility checks).

I'd also advise using a packet tracer of some sort to test out what sort of things are visible (I was originally doing a much simpler verification that meant someone could use a packet tracer to find and duplicate an uploaded score's http request, without knowing the magic string or anything (meant you needed a legit score first, but you could send duplicates of that score as much as you wanted..)). I used Wireshark for testing this.

Huh, this turned out a bit long, but hopefully it helps...

\$\endgroup\$
1
  • \$\begingroup\$ Certainly is. Looks like a salted hash is a good way to go. I hadn't considered that they'd resend the same valid score many times. A GUID and a timestamp included in the packet (and in the hash) should put paid to that - I can check for dupes on the server. Thank you. \$\endgroup\$
    – teedyay
    Sep 30, 2010 at 21:25
3
\$\begingroup\$

I am not an expert in this field but if I were you, I will try to encrypt the score with an key embedded in the your code. Those people will need to apply reverse engineering over your code instead of the plain text used for the web-services.

\$\endgroup\$
6
  • 2
    \$\begingroup\$ I'd go even one further. Generate a hash from the code by hashing the exe file (or part of it). When sending scores send the version number and the server can validate using a table that has simple constants version number->hash code. Then you get the added bonus that if someone cheats by modifying the program, his high score wouldn't count. \$\endgroup\$ Sep 30, 2010 at 21:31
  • 8
    \$\begingroup\$ If someone is modifying the program, they can already send whatever key they want. \$\endgroup\$
    – user744
    Sep 30, 2010 at 22:06
  • 2
    \$\begingroup\$ @Joe Wreschnig, they can send any key they want, but the server should be set to only accept valid keys. \$\endgroup\$ Oct 5, 2010 at 20:55
  • 6
    \$\begingroup\$ I think Joe's point was that they don't have to use the hash of the newly modified program, but can just send whatever the previous hash was. \$\endgroup\$
    – Kylotan
    Oct 6, 2010 at 10:26
  • 2
    \$\begingroup\$ @gd1: how does that help? \$\endgroup\$
    – Kylotan
    Sep 26, 2011 at 14:18
1
\$\begingroup\$

Speedruns are basically recording of every keystroke, and they are recording about a WHOLE game. So, yes, you can record the whole game, it's not unfeasible. Any other way to do that is crackable through reverse engineering (I can't stress it enough: you're not adding security, you're adding obscurity).

Still, even if you do it this way they could actually submit a speedrun. You can do nothing to prevent that.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Usually the playback, not the recording, is the part that is infeasible for the server. If the client's been playing the game for a couple hours, the server-side resimulation could take many CPU-minutes or worse. That's not really acceptable if you have a lot of people submitting scores. \$\endgroup\$
    – user744
    Oct 1, 2010 at 0:11
  • 2
    \$\begingroup\$ The size of the data packet would become an issue for mobile games - especially if the player pays for their bandwidth by the byte. \$\endgroup\$
    – teedyay
    Oct 1, 2010 at 8:13
  • \$\begingroup\$ Oh, what both of you say is true. Still, there's no other "real" solution. \$\endgroup\$
    – o0'.
    Oct 1, 2010 at 9:31
  • 11
    \$\begingroup\$ You don't have to validate every recording submitted, you just have to validate the ones that make it to the top 10. There's not much point cheating to become #11. And you don't have to do it in realtime. Periodic batch process would be fine. \$\endgroup\$
    – gray
    Oct 6, 2010 at 7:21
  • \$\begingroup\$ @teedyay Say the player performs an average of 5 actions per second, and each action can be completely described in 32 bits. That's 20 bytes per second or 72 kB per hour. The typical mobile data price in the USA is $10 per GB, or 1 cent per 1000 kB. \$\endgroup\$ Oct 23, 2015 at 17:27
1
\$\begingroup\$

While you're at it, there's the question of whether a high score is simply a player who found an exploit (for example, if some things in the game give a score penalty, and a bug causes a negative score to "wrap around" to become highly positive... or just a player who finds a certain game condition like a safe spot on the board where they can just sit there and not have to worry about losing, indefinitely).

In order to tell the difference between a hack and a game exploit, uploading at least some play data would be good. It'll help you fix the exploits, at least.

For some games (particularly turn-based ones), you can actually have the game played through the server, where all of the game logic exists on the server side and the client is just an interface. This not only makes score hacking a lot harder, but it also enables you to log all player actions on the server trivially, and thus replay any game at any time. I realize for something like a twitch action shooter, this might be impractical.

\$\endgroup\$
1
\$\begingroup\$

Make all randomness origin from one seed and store the input for every frame. Whenever you have a claimed high score (say top 50), send the seed and the full input to the server. Replay the game on the server and update the leaderboard if you get a high score.

If you consider this unfeasible for more sophisticated games because of the size of the request, take a look at this example.

Say the game has 8 button input (1 pad and 4 buttons) and runs at 60fps. A one hour game input can be transmitted with 3.6KB without compression. Your session will probably have less than one hour and compression should shrink that by a lot, because human input has a lot of redundancy.

The challenge is making the game deterministic, playable from recorded input and executable on the server.

\$\endgroup\$
0
\$\begingroup\$

Never implemented this before but...

Send scores incrementally with timestamps. This gives you a log to see how often the score improved as well as a way to trace the highscore's "momentum".

You would then set milestones/criteria for your scores.

For example: A score greater than 20,000 can not arrive in first 20secs of a game. An score greater than 250,000 can not arrive without an entry greater than 200,000.

This is not exactly the same as sending a game state, but close to it.

Side Benefit: Think of all the useful game play statistics you would get from this. Someone would prob pay good money for that.

\$\endgroup\$

You must log in to answer this question.

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