8

I am having a lot of trouble understanding where I as a developer can validate the data that my users send to me. Let's say for a simple example I have a web page where it has a simple form that contains three fields:

Name -> text field
email -> text field
Lunch Choice -> Dropdown field
  values:
    Burger
    Hotdog
    Pizza

Once the user submits the form their name and order is saved and they can pick up their lunch the next day.

If I am a vicious user and I got to this site and edit the html to say that the pizza value is actually salad and the value of the option is some other value.

Now when the form is submitted and saved, some bad data is going into the database.

Where can I stop this?

My first thought was to have some javascript validation, have an ajax call talk to the server to make sure the values requested are valid. This sounds like a great idea, except javascript runs on the client, not the server, so that is also susceptible to attack.

If I let the user submit the form and then handle the submit on the server and do the checking there, the user has already submitted the form so I would need to do some kind of redirect back to their form?

I'm sure I am not the first developer to have questions about input securities and I was wondering if there is a best practice for handling this type of problem.

Thanks in advance!

3
  • As an aside, if your site obviously requires JS (ie, it shouldn't work without it), you don't really need to handle the redirecting back to the form on validation failure. It can be great for user experience if supporting noscript users is necessary, but let me assure you from experience that it can be a lot more work. It can be a great burden off your shoulders if you simply require JS for the form and let anyone who bypasses that deal with unhelpful errors (<noscript> tags can be used to communicate to users that they need JS).
    – Kat
    Jul 4, 2017 at 21:06
  • As an aside, I should make it clear that supporting users without JS is a good idea. But simply can be a lot more work (especially if you want to return them to the page with the form data partially filled back in and with helpful errors). Obviously a malicious user doesn't need that, but that makes great sense for things like fields that should be required, email addresses that are trivially incorrect, etc.
    – Kat
    Jul 4, 2017 at 21:09
  • langsec.org deals with this very subject. Jul 6, 2017 at 22:00

6 Answers 6

10

Is a basic question. You should validate EVERYTHING on your server side. The back-end is the right place to make validations. Of course javascript validations are useful too to drive standard users into the right values, but for hackers (or any advanced user) is very easy to trick.

It's suppossed you have a database on back-end and you know what kind of food is able to be assigned to each user. You should check if the received value is present on the valid recordset. If is ok you can go on with your workflow. If not, you can show an error and stop the proccess.

Another important problem you'll face is not only the logical bypassing. Beware of special chars and filter everything dangerours like single quotes, double quotes, dashes, semicolons, etc... or you'll face too a sql injection or a XSS (cross site scripting) attack. That is another story... but I mention it because is very important too.

0
5

Oscar already posted a great answer, but in response to some of your questions:

My first thought was to have some javascript validation, have an ajax call talk to the server to make sure the values requested are valid. This sounds like a great idea, except javascript runs on the client, not the server, so that is also susceptible to attack.

That's overcomplicating things. Like you said, this is susceptible to attack and furthermore, is completely nullified when Javascript is disabled in the browser.

If I let the user submit the form and then handle the submit on the server and do the checking there, the user has already submitted the form so I would need to do some kind of redirect back to their form?

Yep.

Traditionally, if you want to use JS validation, you do it to suggest corrections at the frontend. "Hey, there's no @ in your email address. Please enter a valid email address." This saves you the round trip you describe above. Javascript is not for enforcing security, it's for enforcing a better user experience.

Then, once they enter a valid email address and submitted their content, there's no longer any way for them to bypass your mechanisms. The ball is in your court. On the server you check for the nasty things Oscar mentions, sanitize all the strings and if you detect something amiss, you do return the user back to the input page (and if you want to be nice, you repopulate the form fields using the sanitized values).

So at this point, even if JS were disabled, the user still gets an HTML message explaining they need to enter a valid email address. Yes, this does mean crafting two different sets of error messages-- one for JS, one in HTML (which is why I personally never bother with the JS validation).

Just be careful about redisplaying any of the content they provided you. If they embedded malicious code in the strings, displaying them on the resulting page without sanitizing them first can end badly.

4

Any data coming from outside your system is crossing a "trust boundary", and needs to be validated inside your system. That means server-side input validation is required.

Performing input validation means to check your input to be sure you can process it safely. The tricky part is that by validating it, you're already doing some minor processing, and that can create a hidden vulnerability. So there's a specific order to validating input.

The first step is to validate the length of the input. Most input will temporarily land in a finite buffer. Ensure that the amount of input you copy into the buffer doesn't exceed the size of the buffer - if there's too much input and not enough buffer, it creates a classic "buffer overflow" problem; exploiting these is a staple of hackers.

The next step is to ensure the data is in the format you expect. If you're expecting a number, ensure the bytes contain only digits and only the number symbols you permit, such as plus, minus, separator, decimal point, currency symbol, etc. Note that these are locale specific: in the US, a million dollars could be entered as $1,000,000.00, while in Germany a million Euros could be entered as 1.000.000,00€. If you're expecting alphanumeric characters and numbers, use an "approved-list" to accept only the characters you expect.

It's safer to rely on an approved-list of good characters than a deny-list of bad characters, because attackers will learn new attacks in the future. It's possible an unexpected character will permit an injection attack tomorrow that we didn't know about today.

Note that if you reverse these checks and test for special characters before checking the input length, your validation code might be vulnerable to a buffer overflow. That's why it's important to do them in the proper order.

It seems intuitive that input checking should be used to protect against injection attacks (a SQL injection is an attacker entering something bad like ' OR 1=1;DROP TABLE STUDENTS--), but that's not always possible. Someone might try to prevent this injection by putting the apostrophe in the deny-list, but an apostrophe is often valid data, such as in the name O'Brian. Plus an attacker can often work around approved-lists with another strategy like URL encoding. So we add another line of defense in the code that interfaces with SQL. That code needs to be responsible for executing the queries as safely as possible. This could be using parameterized SQL queries, ORMs, or other defensive strategy. That way if attackers figure out a way around the approved-list, the parameterized SQL should still stop them.

Injection attacks aren't limited to SQL, either. Attackers will try injecting path separator characters into file names, shell delimiters like pipes (|), XML delimiters, URLs, etc.; anything that you accept can be subjected to abuse. Any code that interprets user input needs to be written to avoid such problems.

The step after validation is to encode the input in order to protect the output. For example, if you're going to accept < and > and later output the results on a web page, you'll want to make sure you're HTML encoding the symbols so you don't inadvertently create a hole where an attacker can plant a <script>attack!</script> on the output page.

2

To avoid unexpected data being written to the database, validate it on the server side, right after the form is submitted, by the script/process which receives it.

Any data received from external source should be treated as potentially malicious, therefore, every process which receives it should sanitize it before processing.

Client side validation objective is to provide a user-friendly interface. Server side validation objective is to ensure that the user's input is safe.

I recommend OWASP's Input Validation Cheat Sheet and SQL Injection Prevention Cheat Sheet.

1

Validate and Sanitise all user inputs

Theres basically two types of validation, one is essential (Server-side) one is desirable for UX (Client-side)

Client-side validation

Client-side validation that occurs in the browser, before the data has been submitted to the server. This is more user-friendly than server-side validation as it gives an instant response.

Within that there is built in browser based validation and HTML5 Constraints API / JS Based approaches.

  • Built in form validation is done with HTML5 form validation features, and generally doesn't require JavaScript. This has better performance, but it is not as customisable.

  • JavaScript validation is coded using JavaScript. It is completely customisable but also completely overridable and bypassable

Server-side validation

Server-side validation is validation that occurs on the server, after the data has been submitted — server-side code is used to validate the data before it is put into the database, and if it is wrong a response is sent back to the client to tell the user what went wrong. Server-side validation is not as user-friendly as client-side validation, as it requires a round trip to the server, but it is essential — it is your application's last line of defence against bad data. All popular server-side frameworks have features for validating and sanitising data (making it safe).

Credit to the awesome mozilla project for the above text and definitely recommended reading.

0

If I am a vicious user and I got to this site and edit the html to say that the pizza value is actually salad and the value of the option is some other value.

That's not how a form is supposed to work. Your server is telling the user how much things cost, and the user must have no way to influence anything at all. He is being communicated of the price, not asked to tell how much he will pay.

So when he sends the request back to the server, the price cannot be included, only the item code and quantity. So if he edits the HTML to put a negative price on everything, it will still have to pay the original price.

You don't need to validate the price client-side here, but it will be nice to validate if the email is valid. The user will be happy if you catch a mistake before sending the data. But this is a convenience validation, not a secure validation.

You don't write user-supplied data on the database without validation. If the field is a quantity, check if it's a valid quantity: no negative numbers, a reasonable limit, and only integer numbers depending on the item. Validate field sizes, validate email addresses, and never ever print back any user-supplied data without sanitizing before.

If I let the user submit the form and then handle the submit on the server and do the checking there, the user has already submitted the form so I would need to do some kind of redirect back to their form?

Yes. If there's a validation error, bring him back to the form with a message on the top informing what went wrong, and populate every non-sensitive field with the previous values. Don't populate passwords, CVC on credit cards, things like that, but populate as much as possible (after sanitizing). If any field had an invalid value (like putting "FIVE" where it should be 5) you could let the field empty.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.