July 4, 2005, 10:21 p.m.

Bad Practise: Sending back read only HTML form fields

If you had ever written HTML pages sending back form data to a backend server, you must have come across the need to sometimes restrict the user from changing a value - therefore you used a read only form field. This can cause serious trouble...

As an example, consider a system that displays user profile information for editing. Lets say you have a combobox containing roles, and only if you have a higher privilege than the user's record you are trying to amend may you change the field's value. It is nice to display it this way since it takes care of three aspects - firstly, the user's role is displayed (which is nice), secondly if the user editing the record has lower privileges then it is as simple as making the field read only to disable any value changes, and thirdly if the user editing the record has higher privileges then just do not set the readonly flag.

Considering good programming practises, you would normally have one backing object representing the user's profile as an object. This includes the role value. For displaying the record, it is passed to the HTML page (more specifically - the scripting language such as JSP/ASP/PHP/etc) for display. Once the user made all the required changes, all the data is bound to the form backing object and written to the database.

If you are a bit more evolved and not that lazy, you would most probably have implemented a diff-ing system whereby only those fields that changed are saved to the database. Or if not, the simplest is to merely always update all values - since setting one value to the same value does not change it.

The problem comes in when the combobox is read only. It is a trivial matter for anyone to save the raw HTML source of the page to their local disk, edit the combobox field to remove the readonly field, and then submit the form. The backend will happily update the user's role - bypassing the security checks.

The cause of this problem is the age old issue of validating and never trusting user input. In this case, even though the browser is displaying and interpreting the form data, it is quite easy for the user to change the logic and bypass it. Same goes for JavaScript validation. It is trivial to remove any JavaScript validation locally.

The root cause of this problem is that there is only a single layer of security checks, and this layer is in the user's domain. Since this is not a trusted domain, it is fundamentally flawed.

Do not throw away the benefits of JavaScript checks and HTML form field restrictions - just always remember to double check all data on the backend - which is in the server's domain and (hopefully) trusted. The first level security checking provided by HTML and JavaScript allows the user to immediately get feedback on invalid input - much nicer than having to wait for a roundtrip from the server. However, do not rely on the integrity and security they provide. Rather write secure code by double checking all user input again on the backend.