Information Security
javascript api trust single-page-app input-validation
Updated Fri, 03 Jun 2022 15:47:07 GMT

Preventing users from tampering with input


Let's say that I have a single-page web app written in JavaScript and a server-side API, both changeable by me. The app calculates some values based on user input and POSTs these to the API. The values are based on user input but do not contain user input. For instance it might ask the user to pick A or B based on radio buttons, then send their choice to the server. There is no session, and the user is anonymous.

Rule #1 is Never Trust User Input. A malicious user could modify the payload, and change "A" to "C" (not one of the choices). I don't want that to happen.

For simple cases there is an obvious solution: validate the input server-side. If it's not "A" or "B", reject it. In a complex app, though, the number of possible permutations could make validation very difficult.

The app could digitally sign the calculated payload before sending it, but as the JavaScript is available to the client, a user could obtain both the key and the algorithm. Obfuscation isn't sufficient here.

Has anyone come up with any way to prevent this sort of tampering? Time-based keys provided by the server? Web3? Or is there a formal proof that it is impossible (aside from server-side validation against a set of input constraints)?




Solution

TL,DR: It's impossible to do so client side.

Client side validation is just a client convenience, not useful to really validate anything. You don't want the client to mistype his email, putting an invalid char somewhere, and have to wait the form being submitted, the server parsing it, and sending back an error 5 seconds later, you want the error to show instantly. You validate on the client, but you ignore the client validation entirely and validate again on the server.

If the validation is made client-side, the client may submit the request by hand, bypassing all validation. Even if you use hashing, signing, obfuscating or anything else, the result is an HTTP request and the client can intercept it and tamper it.

If your use case is what you asked, server validation it's not difficult at all. Have a table with all questions and all valid answers, and check every client answer with the table. On the first invalid answer you stop the processing and send back an empty page.

the number of possible permutations could make validation very difficult.

You have to choose between server-side (from trivial to very difficult) and client side (not possible at all). I believe the choice is easy.





Comments (5)

  • +2 – I think this makes sense. Even if I had a way of providing tamper-proof signed code to the client with the browser somehow validating that, and then used that to add an HMAC to the message (which would be verified server-side), someone could view (not modify) the signed code to get the key and then sign their own message and post it with cURL. — Mar 06, 2022 at 20:53  
  • +0 – I would add that cURL is effectively a client, where the user can submit anything — Mar 07, 2022 at 08:40  
  • +1 – Regarding code duplication, you would have some between the client-side and server-side validation. I'm not much of a web developer, but there probably are frameworks where you define a "validation", and generate the code for that both client-side and server-side. I vaguely remember such a construct in asp.net. I'd imagine that at least in javascript-based back ends like node.js, you could even somehow share validation as javascript code. — Mar 07, 2022 at 09:58  
  • +3 – You could easily use an app like insomnia or postman and just send whatever you want to your API, so whatever measures you take to validate client side is obsolete. Use server side validation and youre good. — Mar 08, 2022 at 09:41  
  • +3 – Client-side validation is also a billing convenience. Someone has to run the validation code eventually, and if the client's computer runs it and fails, it doesn't send the request, and that's server time you're not paying for. — Mar 08, 2022 at 19:15