cheat prevention for browser based xmlhttp/js/perl/php game

1.1k views Asked by At

Lets say that in a browser based game, completing some action (for simplicity lets say someone clicks on a link that increases their score by 100) clicking on this link which would have a url for example increase_score.pl?amount=100 what kind of prevention is there from someone simply sending requests to the web server to execute this command:

  1. Over and over again without actually doing the task of clicking on the link and
  2. Sending a false request to the server where amount is set to something rediculus like 100000.

I am aware of checking HTTP_REFERER however I know people can get around that (not sure how exactly) and other than some bounds checking for the 2nd option I'm kind of stumped. Anyone ever experience similar problems? Solutions?

10

There are 10 answers

2
Ben S On BEST ANSWER

Nothing can stop them from doing this if you implement your game how you propose.

You need to implement game logic on the server and assign points only once the server validates the action.

For example: on SO when someone votes your question up, this isn't sent as a command to increase your reputation. The web-app just says to the server user X voted question Y up. The server then validates the data and assigns the points if everything checks out. (Not to say SO is a game, but the logic required is similar.)

0
Ahmet Kakıcı On

Token with cookie/session.

0
sclarson On

You could make the link dynamic and have a hash that changed at the end of it. Verify that the hash is correct given that period of time.

This would vary in complexity depending on how often you allowed clicks.

0
Alexandru Luchian On

The logic of the game (application) should be based on the rule to not trust anything that comes from the user.

HTTP_REFERER can be spoofed with any web client.

5
Dean J On

Expanding on Ahmet's response, every time they load a page, generate a random key. Store the key in the user session. Add the random key to every link, so that the new link to get those 100 points is:

increase_score.pl?amount=100&token=AF32Z90

When every link is clicked, check to make sure the token matches the one in the session, and then make a new key and store it in the session. One new random key for every time they make a request.

If they give you the wrong key, they're trying to reload a page.

2
Jack M. On

I would suggest making a URL specific to each action. Something along the lines of:

/score/link_88_clicked/
/score/link_69_clicked/
/score/link_42_clicked/

Each of these links can do two things:

  1. Mark in the session that the link has been clicked so that it wont track that link again.
  2. Add to their score.
1
BlairHippo On

Short version: you can't. Every piece of data you get from the client (browser) can be manually spoofed by somebody who knows what they're doing.

You need to fundamentally re-think how the application is structured. You need to code the server side of the app in such a way that it treats every piece of data coming from the client as a pack of filthy filthy lies until it can prove to itself that the data is, in fact, plausible. You need to avoid giving the server a mindset of "If the client tells me to do this, clearly it was allowed to tell me to do this."

WRONG WAY:
Client: Player Steve says to give Player Steve one gazillion points.
Server: Okay!

RIGHT WAY:
Client: Player Steve says to give Player Steve one gazillion points.
Server: Well, let me first check to see if Player Steve is, at this moment in time, allowed to give himself one gazillion points ... ah. He isn't. Please display this "Go Fsck Yourself, Cheater" message to Player Steve.

As for telling who's logged-in, that's a simple matter of handing the client a cookie with a damn-near-impossible-to-guess value that you keep track of on the server -- but I'll assume you know how to deal with session management. :-) (And if you don't, Google awaits.)

0
Peter Bailey On

A few things to note here.

First, your server requests for something like this should be POST, not GET. Only GET requests should be idempotent, and not doing so is actually a violation of the HTTP specification.

Secondly, what you're looking at here is the classic Client Trust Problem. You have to trust the client to send scores or other game-interval information to the server, but you don't want the client to send illegitimate data. Preventing disallowed actions is easy - but preventing foul-play data in an allowed action is much more problematic.

Ben S makes a great point about how you design the communication protocols between a client and a server like this. Allowing point values to be sent as trusted data is generally going to be a bad idea. It's preferable to indicate that an action took place, and let the server figoure out how many points should be assigned, if at all. But sometimes you can't get around that. Consider the scenario of a racing game. The client has to send the user's time and it can't be abstracted away into some other call like "completedLevelFour". So what do you do now?

The token approach that Ahmet and Dean suggest is sound - but it's not perfect. Firstly, the token still has to be transmitted to the client, which means it's discoverable by the potential attacker and could be used maliciously. Also, what if your game API needs to be stateless? That means session-based token authentication is out. And now you get into the deep, dark bowels of the Client Trust Problem.

There's very little you can do make it 100% foolproof. But you can make it very inconvenient to cheat. Consider Facebook's security model (every API request is signed). This is pretty good and requires the attacker to actually dig into your client side code before they can figure out how to spoof a reqeust.

Another approach is server replay. Like for a racing game, instead of just having a "time" value sent to the server, have checkpoints that also record time and send them all. Establish realistic minimums for each interval and verify on the server that all this data is within the established bounds.

Good luck!

0
brian d foy On

It sounds like one component of your game would need request throttling. Basically, you keep track of how fast a particular client is accessing your site and you start to slow down your responses to that client when their rate exceeds what you think is reasonable. There are various levels of that, starting at the low-level IP filters up to something you handle in the web server. For instance, Stackoverflow has a bit in the web application that catches what it thinks are too many edits too close together. It redirects you to a captcha that you need to respond to if you want to continue.

As for the other bits, you should validate all input not just for its form (e.g. it's a number) but also that the value is reasonable (e.g. less than 100, or whatever). If you catch a client doing something funny, remember that. If you catch the same client doing something funny often, you can ban that client.

1
Nils Munch On

If you want the game to only run on your server, you can also detect where the signal is sent from in your recieving trick, and ignore anything not coming from your domain. It will be a real pain to tamper with your codes, if you have to run from your dedicated domain to submit scores.

This also blocks out most of CheatEngine's tricks.