10

I'm working on a web app where certain actions earn a user 'karma'. The karma increment is a simple AJAX request that increments a number in the DB. Now whats to prevent a user from making duplicate AJAX requests manually and increment their own score again and again?

Edit: The activity that earns the user karma is all happening client-side using javascript. So there is no way for the server to know if its a 'real' request coming from the app or a 'fake' request created by the user typing $.post on the console.

Edit2: Found a similar SO question dealing with this problem. Sounds like the answer is, security by obfuscation is the best bet. Guaranteed security is not possible. So any suggestions for encryption tools etc. that would make the content of the AJAX calls harder to read would be appreciated.

Community
  • 1
  • 1
udit
  • 2,745
  • 3
  • 33
  • 44
  • Is the user must be logged in to increase a karma? – Dougui Jul 12 '12 at 14:39
  • Yes, they must be logged in to increase karma. I'm not worried about unauthenticated users, but I'm trying to prevent a logged in user from manually (using javascript in the console) updating his own karma. – udit Jul 12 '12 at 15:30
  • I think you answered your own question in your edit. – Marcus Adams Jul 12 '12 at 15:52
  • Don't let users increment their own karma (why should they?) – arnep Jul 12 '12 at 15:54
  • @amep. There is no button for the users to increment their own karma. I'm trying to prevent a savvy user from inspecting the NET tab, realising what AJAX call (initiated by the app)increments karma scores and replicating the same call in the console. – udit Jul 12 '12 at 16:02
  • 1
    best related so far is http://stackoverflow.com/questions/73947 – Saic Siquot Jul 12 '12 at 16:50

5 Answers5

1

Rather than trying to stop people from cheating, you should focus on minimising the adverse effect that cheaters will have on non-cheaters:

If karma is being used as a 'high score' and you're worried about the user earning karma faster than they should then consider keeping a timestamp of the last time they earned karma and reject a request if it's too soon, and/or set a daily limit on karma earned so they can't automate the process when they're not at the keyboard.

If the action that earns karma for a user also has an effect on other users then it should be one ajax call that triggers both effects.

DaveMongoose
  • 875
  • 9
  • 15
0

There are a couple of solutions for this problem. One of them is to "disable" your link or button or whatever you click to send AJAX request.
If you use JQuery you have some callbacks for this: beforeSend and success. You may try to "disable" your trigger in beforeSend callback and them "enable" it in success (or complete) callback.

$.ajax({
  url: "/example",
  beforeSend: function(){
    #disable trigger
  },
  complete: function(){
    #enable trigger
  }
})

But I think it is kind of hack :)

bor1s
  • 4,081
  • 18
  • 25
  • I think there is no button involved here – Saic Siquot Jul 12 '12 at 14:40
  • Luis is correct, there is no button involved here. I'm just trying to protect against someone pulling up the developer console and doing a $.post("/my-url", {karma: 45}) – udit Jul 12 '12 at 15:23
0

your server should keep track of client activity, in order to deduce if the execution of the ajax call is "logical" or "fake". This can be done with sesions (not recommended), because can be faked too (depending on some factors), or directly on the db

Saic Siquot
  • 6,513
  • 5
  • 34
  • 56
  • This can be a bit hard..since the client activity that I'm tracking is all client-side. Think of it like a game which is executed only on the browser using Javascript. I could keep 'states' in the session or the DB which could mark the beginning or end of a game and disallow any Karma updates outside of the window but that wouldnt help since 1) Maintaining that state involves making AJAX calls that the user can make themselves. 2) It still doesnt prevent the user from manually posting ajax calls to update karma while the game is running. – udit Jul 12 '12 at 15:29
  • I full understud your senario, I think there are only mitigating tactics, but not "a solution" for this. I will be reading as you anwsers here. +1 to your question. I recommend to change "duplicate" by "fake" on the title. – Saic Siquot Jul 12 '12 at 15:47
  • Thanks Luis..Title changed as per suggestion – udit Jul 12 '12 at 15:48
0

In general I'm not comfortable handing out security advice, but since this is simply for incrementing karma in an app I'm going to give it a shot.

How about using an HMAC digest? You'll need to generate a key for clients when they login to the app and the client will need to send three bits of data when incrementing karma:

  1. Who they are (username).
  2. Who they want to increment (username).
  3. The HMAC digest of the key you gave them at login and the concatenation of both usernames.

The server then looks up the key for the username in (1) above and compares the HMAC digest of the loaded key and the concatenation of the usernames with the provided HMAC digest in (3) above.

You can outright reject cases where the usernames in (1) and (2) compare equally - this will prevent users from incrementing their own karma.

You can also reject cases where the digests don't match - this will catch cases where a user tries to spoof a karma increment request.

If the key that you shared at login is intercepted, it could be used by another user to generate false karma increments.

You should be able to find HMAC implementations for both Ruby and JavaScript to implement this.

dowski
  • 3,188
  • 2
  • 20
  • 16
0

You could check if the logged in user is the karma owner, with something like this :

if current_user.id == karma.user_id
  #... throw error
else
  karma.save
end
Kev
  • 118,037
  • 53
  • 300
  • 385
Dougui
  • 7,142
  • 7
  • 52
  • 87