See all Insights

Keeping Your Site Safe from the Bad Guys

One thing that has evolved almost as quickly as the web itself is the rise of security problems associated with it. Web developers today have to take great care in not introducing security holes into websites. These security lapses can come from something as seemingly innocuous as a simple contact form.

Security weaknesses on the web generally come in three major forms, cross-site scripting (XSS), cross-site request forgery (CSRF), and SQL injection. These acronyms may sound intimidating, but the principles behind them are fairly simple to understand.

1) Cross-site Scripting (XSS)

XSS is basically allowing someone to execute their own Javascript in the context of your site. If a malicious person manages to insert their code to run on your page and you visit that page with the malicious Javascript on it, the hacker can fetch the user’s cookie, and as most sites use cookie-based authentication, this means that the villain can now appear to be you on the vulnerable site. Not good.

The hard part for the malicious user is getting his evil-doing Javascript onto the attacked website in the first place. The number one place where security breaks down is through forms on your website. Often, poorly-built web forms will display on a page whatever information the user puts into a web form. A good example of this is a search box. A common user will type in his or her search query and then on the results page it will say something like “Found 13 results for x” where x is what the user typed in. When the user plays by the rules, this is not a problem. The issue arises when a malicious user carefully crafts their search query to include HTML that gets printed out exactly the way it is put in. When this happens, the bad user can put their own Javascript on your site.

For a major type of XSS, the malicious user gets an unsuspecting victim to click a link. This link will do the insertion of the Javascript in the victim’s browser, and when the Javascript is executed, the villain can do anything from having any forms on that page get submitted to him, redirect the page itself, or steal the cookie used to authenticate a user to a website.

The key to avoiding this type of attack is to never, ever, ever trust user input and always assume the worst. The biggest majority of XSS holes can be closed by simply escaping the HTML special characters (<, >, and &). If you escape the greater-than and less-than signs by converting them to their HTML entities, you close off almost every chance of the user inserting their own HTML/Javascript on to your page.   

2) CSRF

Cross-site request forgery is when a user, specifically a logged-in user, makes a request to another website unknowingly.

Let’s say you have a button inside of your web application or CMS that deletes your account. Now a malicious user on another website could set up a button on his website that appears to be an inncocent button that he states takes you a game download page, but in fact is a copy of the delete account button on the original site. If you are logged into the original site and then hit the button on the malicious person’s site, this delete process may execute just as if it was coming from the actual site. If the original form processing code doesn’t take extra care to verify that the form submission is indeed coming from where site is expecting, bad things could happen, like your account getting deleted.

CSRF doesn’t only happen through form submissions. This can also happen through normal URLs if the URLs modify the state of your data (e.g., change username, delete account, submit a link to a social media site).

One way to fix this is to have a secret key that is submitted along with the form in a hidden variable, or a secret key as a URL parameter. This should be something like an MD5 hash of the concatenation of the user’s ID and a secret password known only to the internal code of the site. When the processing is done for a request, your processing code will make the sure the user is logged in, replicate the original hash, compare it to what was passed along in the form, and then, if they still match, process the request.

 

Another way to defend against this is to check the Referer header of the request. It probably best to use the hidden token method and Referer check together as there is no such thing as too much safety, and the code is not significantly more complex.

To sum up CSRF, any time a request is received that depends on the user being authorized, extra work must be done that verifies that the request is coming from your site, and not some other mailicious party.

3) SQL Injection

As soon as websites started to become driven by databases, SQL injection vulnerabilities appeared. SQL injections are when a user’s data causes a database query to differ from its expected function, usually in a bad way.

An example:
$user_id comes directly from user input, probably via a form.

Your query:
DELETE from user_accounts WHERE user_id = $user_id;

Now if $user_id in fact equals the user’s ID, you’d be in a good shape. This is a huge hole, though. What if the user passed in another number or, even worse, something like “1 or 1”?

The executed query would look like this:
DELETE from user_accounts WHERE user_id = 1 or 1;

This would delete every single row from your user_accounts table. Ouch. Obviously, this is a worst case example, but not by much.

This may seem obvious and you may put in some safety checks. You would probably make sure user_id is a number and have some additional verification that this number hasn’t been tampered with. The issue in preparing against SQL injection is that you constantly have to keep track what data you should be getting and inserting this safely into your query. This usually means taking care of single and/or double quotes, commas, type checking (am I expecting a number or a string?), as well as SQL keywords. This is a lot to remember and it’s prone to user error.

Luckily, almost every modern database has a piece of functionality called “parameterized statments” and most languages have an interface to use them. Prepared statements let you build your query, putting in placeholders where your data goes, and then the SQL engine will build out the query in its syntax tree, and then as a last step before executing, it will substitute in whatever data you passed in, without making it part of the query itself.

Basically, this means that the parameter data and the code of your SQL query are kept separate and you don’t to worry about your data changing your code, or vice versa.

Security is one of the things that developers do that when taken care of properly, the client never notices, but if something slips up, it (or the lack of it) comes front and center. The main source of security holes on a website, with a little extra work, can be easily avoided.

Related Posts