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)
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.
$user_id comes directly from user input, probably via a form.
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.