15

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)?

5
  • 90
    Just validate it on the server side.
    – Polynomial
    Commented Mar 6, 2022 at 2:51
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – Rory Alsop
    Commented Mar 7, 2022 at 17:44
  • 7
    The benefit of client side validation is that when you catch the bad input server side you know someone has been messing with their client. Commented Mar 7, 2022 at 22:02
  • Depending on the data binding, form or web framework the validation comes for free and you do it declaratively.
    – eckes
    Commented Mar 8, 2022 at 8:49
  • 3
    Client-side validation is only intended for rapid feedback to the user, not for final input validation. Commented Mar 8, 2022 at 17:35

6 Answers 6

88

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.

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.
    – TrueWill
    Commented Mar 6, 2022 at 20:53
  • 11
    I would add that cURL is effectively a client, where the user can submit anything Commented Mar 7, 2022 at 8: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.
    – Jonathan
    Commented Mar 7, 2022 at 9: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.
    – Alex
    Commented Mar 8, 2022 at 9: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. Commented Mar 8, 2022 at 19:15
25

You'll have to validate it on the server side. You say that the number of possible permutations is too large - but the client side somehow did it, didn't it? If the server side doesn't have enough information to efficiently validate the input, then have the client send more info. Worst case scenario - just send all the raw user inputs and re-calculate everything on server side, ignoring the client side work entirely.

1
  • 5
    This points to another possible weakness of the OP architecture: the fact that it's relying on the client to do computations that the server then assumes valid. he overall solution is probably going to be easier if the Server recalculates everything from the input fields.
    – Rad80
    Commented Mar 7, 2022 at 21:50
5

The only way you can handle this is by authenticating the connection and controlling what happens in the browser. Authenticating the connection is of course possible, but to control the browser is another matter. You'd have to trust both the OS and the browser, and avoid leaking any authentication keys.

All in all: no this is not really possible, at least not for generic users. It may be possible to a certain extend if the computers are controlled by trusted entities. However, in general you should maintain a secure state on the server, and that includes validating input in case that can trigger such an invalid state.

2

Just a few notes:

Obfuscation isn't sufficient here.

Any client-side validation is essentially just as good as, and in a stretch of imagination can even be considered, obfuscation.

In a complex app, though, the number of possible permutations could make validation very difficult.

Validation is hard. I'd go so far as to say that validation can be one of the most difficult and persistent obstacles that web developers face. Too often do bad actors attempt to infiltrate any area of input. They're all attack vectors.

Unfortunately, systems which require complex validation often need custom solutions, but there are a few patterns and frameworks you can follow. For example, you can create infinitely customizable validators using ASP.Net. Here's a start if you're curious:

https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-6.0#model-state

1

A separate security device may work. Such a device should contain the private key that is impossible to get outside the device, own independent keyboard and a small CPU that can internally apply the internal private key for digitally signing the input. The signature then can be verified using external public key but the signed content cannot be forged. Something must also be done against replay attack (maybe include some single-use random sequence as part of the signed content).

In the simplest case the user can read the signed content on the single line screen of the device and type into the form of your website, so you do not need to program also drivers. The input could be intercepted and modified at this point, but then it will be wrongly signed.

Some banks use comparable devices for authentication in they websites. They must be robust against reverse engineering but they only need to discard the private key if being tampered.

5
  • Fair, but unrealistic in many cases. For example Stack Exchange isn't going to mail a YubiKey to every one of its users (although they could support them for users that owned one).
    – TrueWill
    Commented Mar 7, 2022 at 15:48
  • 1
    Yes but my bank have sent me such a device.
    – h22
    Commented Mar 7, 2022 at 17:33
  • 4
    Your bank sent one to you because they lose A LOT of money if your account is compromised. Very few sites are on this category...
    – ThoriumBR
    Commented Mar 7, 2022 at 20:42
  • 1
    Still, what keeps you from completely understanding the site's javascript and initiating the OTP and using the valid token/session yourself? It just makes it harder but not impossible
    – Tvde1
    Commented Mar 8, 2022 at 10:02
  • The signature must be re-validated on the server side of course. But also the browser can pre-validate as the public key is not secret.
    – h22
    Commented Mar 8, 2022 at 10:24
1

Also, apart from the necessity of validation server-side to have any hope of security do NOT attempt to validate by any "economical" RegEx expressions.

Even if the legit user would only send you a certain class of strings, the bad people can send you "strings" with bad characters, which are too long, etc., so you do NOT want to give user inputs any leeway.

RegExp testing is subtle enough (DFA or not? greedy matching or not? ...) that it's not the way to go even if you have an idea about it. Make a look-up with hard-coded legit inputs.

1
  • 1
    As the question already notes, "hard-coded legit inputs" works if your legit inputs are as simple as "A or B", but the real world tends to have harder problems. Having said that, you can probably check the input length before you feed it to a regex. Bad characters? That's not a problem for a regex to detect. [a-z]* will reliably reject a quote or curly brace.
    – MSalters
    Commented Mar 8, 2022 at 14:39

You must log in to answer this question.

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