22

I'm a little bit of an amateur on API security. I'm building a browser-based puzzle with a leaderboard, and I'm wondering what prevents a user from simply hitting the /success endpoint with data that basically equates to { time: '3s' } automatically putting them at the top of the leaderboard without even actually finishing the puzzle.

If they just fetch from the console, what's to stop them from falsifying their result? The headers can all be faked to look authentic, right? And from the console it would still be "coming from your webpage" so it would pass a whitelist. So how is this handled?

7
  • 32
    Never trust a client you don't have sufficient control over, which basically means never trust a something running in the clients browser even if you hope that this is your app. Does this answer your question? Is it possible to ensure client data is correct? Commented Mar 1 at 6:57
  • 17
    Remember; The UI of your website is an entirely optional way for users to interact with your APIs. If they want to use any other front end that's fine. Believing anything else will lead to security disasters Commented Mar 1 at 16:18
  • 2
    1. Your users are authenticated with some sort of credentials, right? 2. Authenticated users cannot just post scores, the server knows what time the game started, and the /success endpoint just subtracts now from the start time to calculate how long the game took. It's now impossible for a user to fake how long a game took.
    – Neil
    Commented Mar 1 at 16:55
  • 17
    Nothing stops them. In fact nothings stops them from hitting your endpoint with arbitrary data without the use of your webpage code.
    – Bergi
    Commented Mar 1 at 17:04
  • 14
    You have good thinking skills to ask this question. You're going to do well in security. Commented Mar 1 at 17:14

3 Answers 3

71

While Garret Albright provided a suggestion for this particular case, I think it's important to stress the underlying problem: Nothing prevents the user from sending fake data to the API. This is why you can never trust user input. Anybody can send anything they want to any endpoint, and there's no way to tell whether the request was performed by your JavaScript code or a user playing with the JavaScript console or a program which is completely independent from your website (think of tools like cURL). The resulting HTTP requests will all look exactly the same. This is probably the most important insight in web security.

Any data and any action that matters must come from the server. You cannot trust the client to report that they've finished the puzzle and how much time they needed, so the server must measure the time and determine whether the puzzle was solved. The only data you can accept from the client is their suggested solution and possibly their nickname for the leaderboard.

The rule of not trusting user input will become even more important when you insert the input back into the web page (e.g., to display the username on the leaderboard) or store it in a database. That's when you'll run into all kinds of classical web security problems like cross-site scripting and SQL injections.

3
  • 43
    “never trust user input” — this deserves to be in bold, capitalised, and in as large a typeface as possible!
    – gidds
    Commented Mar 1 at 16:44
  • 9
    As an addendum, any data generated by the server can only be trusted if it has stayed within the server or has been signed. If you send it out to the client and then round trip it back to the server, it can no longer be trusted (unless signed) since it could be modified during that trip.
    – Bob
    Commented Mar 1 at 23:45
  • 2
    My approach: ALL data from a client wants to hack/infect/mess with you! And if it didnt, you were just lucky :)
    – Martijn
    Commented Mar 4 at 9:46
22

You have the server store the date and time that the puzzle is requested by the browser, then have the client-side code submit the solution that the user found to the server, not the amount of time it took. Your server-side app confirms the solution, and if it's correct, it stores the completion time. The difference between the request time and the completion time is the time it took for the user to solve the puzzle.

6
  • 14
    This works as long as puzzles are unique. If a puzzle is re-used at some point an attacker can do one normal-speed solve, then re-request the puzzle and submit their pre-computed solution almost instantly. Of course, the next problem is going to be detecting puzzle solving robots.
    – 9072997
    Commented Mar 1 at 14:37
  • 1
    @9072997 It's easy for servers to keep track of which puzzles they've given to a user and avoid giving out duplicates.
    – Barmar
    Commented Mar 1 at 16:24
  • 11
    @Barmar And then the user has two different accounts, on two different computers using two different public IP addresses :-)
    – jcaron
    Commented Mar 1 at 17:16
  • 13
    That's an entirely different problem, unrelated to the ability to forge client responses. There are lots of ways to cheat in online games, like colluding with another player.
    – Barmar
    Commented Mar 1 at 17:18
  • 4
    @Barmar Or guessing the output repeatedly. Leaderboards only remember the 1 time you guessed right, not the millions of attempts you failed. Saw that in action just a few weeks ago.
    – Mast
    Commented Mar 2 at 9:17
8

Regrettably: simply not possible with a game of skill.

If all you have is start time and end time, there is nothing stopping me from writing a script to start the puzzle, autosolve it, and submit the answer, all within milliseconds. IF you block submissions in milliseconds, I can just do a binary search upwards to find a time you will accept, and submit at that time instead.

If the puzzle has "moves" (for example, a sliding block puzzle, jigsaw puzzle, etc), then you can get some slight modicum of security by making the server validate each move, so that my solver then has to submit each required move to solve it. But there's no reason I can't do that.

You can add a few additional layers to the security onion by obfuscating your code, encrypting the communications, checksums, sending every mouseclick to detect nonhumans, detecting fake scores and showing them only to the faker so they don't know their faked score has been disallowed, etc... but ultimately, you need to display the puzzle to the user, and accept inputs from the user that can solve the puzzle, and that's all a solver script needs.

Macros like AutoHotkey or any keyboard and mouse hooking program will allow your solver script to read the screen and fake the keyboard and mouse input.

The only way, in gaming contests, to get someone to provably win without automated tools is to have them sit in front of you, with a machine and controller you provided them. That's why prize-money gaming events are usually done in person.

But there IS a way to make it fair anyway! Blind randomness with no advantage for time, and all gameplay moves handled by the server. So poker websites can work, because what cards you have and your AI opponent has are blindly random (so long as you don't do anything dumb like tell the client about any of the cards the payer cannot see). Once the basics of the statistics are known, you can't really play a game like poker any better with the help of a bot. Same with most other gambling games: slots, etc.

[Edit: As @Joshua points out in comments, the above is only true of single-player gambling, or multiplayer methods of gambling for which collusion between players cannot become just another form of trust-the-client vulnerability. I'm kinda surprised that anyone thought multiplayer online card games could possibly be secure.]

[And, of course, if there's money in it, make sure that your randomness REALLY IS random if you take that path. Just calling rand() and hoping ain't enough. If you can find a copy of Laura D Hamilton's seminal "When Random Isn't Random Enough: Lessons from an Online Poker Exploit", it's absolutely worth reading: I can't find it online any more, sadly.]

9
  • 1
    Very good answer, and not entirely true at the end. For chess already bots play much stronger than humans. For pocker I read that they made something good for 1-1 games. So perhaps it is not yet a clear win. But I'm sure some people are working hard on it and will be least surprised when it's done :) Of course for chess games there are rigorous bot detection algorithms. I think for pocker will be harder to distinguish bots from humans though. Commented Mar 3 at 17:19
  • 3
    It doesn't work anymore; poker websites are not securable, period. The problem is the 75% attack. They gather up cheap labor to pass bot checks and run mostly automated bots that share information and flood the servers using most of the slots at the tables. The bots don't care which bot wins only that one of them wins and know each others hands. It's not possible to beat even if the server is perfect. Thus the bots walk of with money and the actual players are ripped off.
    – Joshua
    Commented Mar 3 at 19:44
  • 1
    Captcha bypass is a solved problem about 10 years ago. You can easily and readily buy captcha solves @ thousands per dollar, so there's no way to actually filter them out. The solves are also done by an actual person anyways combined with an AI, just outsourced to someplace ridiculous and the person literally just sits there solving captchas.
    – Nelson
    Commented Mar 4 at 6:35
  • 1
    @Joshua It's not possible to secure against collusion at all, and when scaled up, legit players lose. The bots will collude and sit at the same table, then a real player joins, and that poor guy has no chance. It doesn't matter if the bots lose their blinds because their own team is winning the pot at the end.
    – Nelson
    Commented Mar 4 at 6:37
  • 2
    @DBS If there's money to be made, people will do it. Forget something lucrative like online gambling, people were farming gold in MMOs to sell. The payoff for poker is so much higher than gold-farming in MMOs by orders of magnitude.
    – Nelson
    Commented Mar 4 at 15:22

You must log in to answer this question.

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