Never Trust Your Users

April 27, 2011 by Noah
I realize the title of this blog is basically Rule #1 about web application security, but sometimes this rule needs to be applied in some less-than-obvious situations.

Here are some of my anecdotes from the times when I've personally been bit by this, so that hopefully you won't repeat my mistakes and will be more aware of just how many ways users can try to get past your site's defenses.

Don't trust your forms

This is the most obvious place you shouldn't trust input coming from your users. Anything the user fills out in the form must be validated on the server side, regardless of what restrictions you may have done on the client side.

My anecdote: I once wrote a super simple guestbook in Perl. It had three form fields: a name and an e-mail which were both single-line text boxes, and a textarea which took multiple lines of text. Guestbook entries were stored in a flat text file, with one line per entry, so it looked like this:

Dave||Hey, nice site!
Mike||Hey, just leaving a guestbook entry!
Obviously, I stripped all HTML code out from all the form fields, in order to protect myself against an XSS attack. But, I figured, the "name" and "email" are text boxes that can't have multiple lines, and so I only filtered multiple lines from the "message" (I substituted them with a "<br>" instead, after removing other HTML, so they would display with multiple lines on the "view guestbook" page).

How I got bit: One of those Dumb Submitter Bots found my guestbook and spammed every field on the page with their multiple-line junk mail including hundreds of links to sites that are sure to infect you with a virus.

So, my guestbook.txt started to look like this:

Dave||Hey, nice site!
Mike||Hey, just leaving a guestbook entry!
Rolex watches 80% off!

[url=spam site 1]click here![/url]
[url=spam site 2]click here![/url]||Rolex watches 80% off!<br><br>[url=spam site 1]click here![/url]...
The result wasn't too bad though: just a few guestbook entries displayed on the page that shouldn't and made the page look broken.

Lessons learned: Never, NEVER trust your form inputs. Literally ANY type of data can be sent under ANY of your fields. To make it clear, even your <select> boxes aren't safe. You may think you limit your user to sending only one of a few options when they submit the form, but they can still easily submit anything else in that field.

Be careful with filesystem access

On one of my first content management systems, I had links with URIs that looked like this:
My site was coded to take this parameter and open a text file named "about.txt" to get the content of the page the user wanted, something along the lines of:
my $page = "./private/pages/" . $q->param("p") . ".txt";
One of my friends kindly broke this for me and told me what he did. What he did was linked to a page with a URI that looked more along the lines of this:
And so the file my site was opening was "./private/pages/../users/admin.txt", or, more canonically, "./private/users/admin.txt"; and so, he was able to download the private user information for my admin user, including the password (I don't think I even hashed my passwords back then, either). Bad!

Any user-supplied data that is going to be used to access the server filesystem should be thoroughly filtered. Nowadays I would use a regular expression like this:

$p =~ s/[^A-Za-z0-9\.\-]//g;
Stripping out everything that isn't a number, letter, period or dash.

Watch out for HTTP Referrers and User-Agents!

On one iteration of my site, I had pages that would list the most popular web browsers/crawlers, and the list of links on the Internet that link to my site. I was getting this information from the "User-Agent" and "Referer" headers that a browser sends when they request a page from your server.

This is an unexpected vector of attack. User-Agents and Referers are just as easy for a malicious user to edit to anything they want as a form field. So, once I was viewing my User-Agent page and I got a JavaScript error in my browser. Investigating, there was a bit of broken JavaScript code on my page, inside my list of User-Agents!

It was along the lines of this:

<script>window.location = " + document.cookie;</script>
The idiot cracker made a syntax error though which broke the script instead of letting him steal my session cookie. But, it just goes to show that you can't even trust browser headers.

Beware of X-Forwarded-For

This one I haven't been bitten by personally but a security researcher I know tipped me off about it.

A great many web applications rely on the environment variable REMOTE_ADDR, which contains the remote user's IP address. Web apps log your IP along with the things you post on the site, so that if they need to ban you for spamming, they're able to ban you by your IP address.

But this breaks when you get proxy servers involved, because a proxy server requests your web app on behalf of numerous users on the other side, and if you ban the proxy server's IP address, you ban a lot of innocent users. So, a lot of proxy servers will send an "X-Forwarded-For" header to your server which contains the IP address of the user behind the proxy.

So, a lot of poorly coded web apps will prefer X-Forwarded-For instead of the REMOTE_ADDR, for example by using code like this:

But, X-Forwarded-For should be considered user-supplied information. A malicious user can set this header to anything they want, which means they can try embedding HTML code in it (so when your web forum shows the IP address to your admins, the HTML code executes instead), or they may merely fake their IP address with it (like setting it to the localhost address,

Anyway, I hope after reading this you'll keep in mind that the phrase "never trust your user" extends even to some pretty unusual places.



There are 0 comments on this page. Add yours.

Add a Comment

Used for your Gravatar and optional thread subscription. Privacy policy.
You may format your message using GitHub Flavored Markdown syntax.