Never Trust Data from the Browser

"The browser is the most hostile programming environment in the world." - Douglas Crockford

A Budapest teen was arrested this month after being accused of "hacking" (source).  

What did he do?   

He went into his browser's Developer Tools and changed the price in a form field to see whether he could purchase a monthly public transportation ticket at a discount.   It worked, and he promptly reported the issue to the company.

Setting aside the heavy-handed response by the company and local authorities, let's explore how the ticketing system allowed this price manipulation to happen.

The system ignore a cardinal rule of information security: Never trust anything coming from the client

In this case the client was the browser.   There's no way to prevent users from changing information submitted from their browser to a server.  As developers, we have zero control over this. 

Form Validation

Client-Side Validation

Client-side validation is meant for usability not security, so it would not have helped here.   This type of validation helps the user by providing immediate feedback as they fill out the form.

To bypass this, the user could modify the JavaScript in their browser or disable it entirely.   Or they may not even be using a browser at all.   They could use a tool like curl or Postman to submit a POST request with form data.

Client-side validation is still important for UX.  Below is an example of good client-side validation from Mint.com.   The form provides both positive and negative feedback to the user.

Server-Side Validation

To prevent the user from setting a weak password or changing a product's price,  we must perform server-side validation.  This provides the actual security which was lacking in the ticketing system.

We can verify that the data conforms to the application's expectations.  Some common checks would be to verify that all required fields have valid data and to use some type of prepared statement before executing a database query.  

The Checkout Form

The site's checkout form probably looked something like this:

<form method="post" action="/order">
  <input name="description" value="Monthly Ticket">
  <input type="hidden" name="price" value="50.00">
  <input type="submit" value="Order">
</form>

This is poor form design because anyone can change the price value to whatever they want before submitting the form. However, even with this form, server-side validation should have caught that the price was incorrect.

Here's a better version:

<form method="post" action="/order">
  <input name="description" value="Monthly Ticket">
  <input type="hidden" name="product-id" value="1000">
  <input type="submit" value="Order">
</form>

We aren't including the price in the form anymore.   When the form is submitted, we retrieve the price of product ID 1000 from a database.

Sure, the user could change the product ID to something else, but that would either be an invalid product or a different product.

Additional Reading