Dev Principle #7: Avoid Common Code Vulnerabilities
There will always be hackers looking to get into your website. Whether it is to deface it with their branding/cause, gain access to your data, or even turn your site into a distributor of malware, it’s important to make sure your code is secure against common threats. Here are a few of the most common vulnerabilities and suggestions for the best way to prevent them.
One of the oldest vulnerabilities (and one of the most common today) stems from your application’s connection to the database. When you’re coding things on the fly, it can be tempting to build an AdHoc SQL query where you simply append your query parameters into a SQL statement:
[code]”SELECT * FROM [MyTable] WHERE [name] = ‘” + myQueryName + “‘ ORDER BY [created_at] DESC;”[/code]
Without any proper sanitizing of your database variables, it is easy enough to add a separate ‘ to the query parameter and add your own SQL code to the mix. The database winds up seeing a statement like this:
[code]”SELECT * FROM [MyTable] WHERE [name] = ‘foo’; SELECT user, pass FROM [users] WHERE ‘1 = 1‘ ORDER BY [created_at] DESC;”[/code]
This can result in a scenario similar to the infamous “Bobby Tables” comic.
The most common solution is to use a “parameterized query” when generating your SQL query. All of the mainstream languages that “talk” directly to a database have this. The process involves setting up your query with variable placeholders, thus letting the managing code escape any system characters into their “plain text” counterpart. This cheatsheet has examples for most of the common languages today. You can find more online.
Another solution is to leverage an Object Relational Model (ORM) library to handle your data access. ORMs – such as Entity Framework, ActiveRecord, and Eloquent – have grown in popularity because they make it quick and easy to scaffold your code against the database back-end for all your Create Retrieve Update Delete (CRUD) operations. These ORMs use parameterized queries by default, so there is no additional work to be done once the scaffold is in place.
Similar to SQL Injection, Cross Site Scripting (XSS) attacks occur at the browser level. Instead of using bogus data to blow up the database, the bogus data is stored in the database with the intent to infect other users who visit your site. For example, if you filled out your user profile for a site and used ‘<script>alert(‘Hello World’)</script>’ as your first name, the page that is rendering the list of users for a site could wind up sending an alert message to all those who view the site. If you tweak the script even more, you could inadvertently redirect the user to a malicious site. Highly sophisticated versions of this attack will inject fully running scripts that are hidden behind an actual value so the user (or the code owner) won’t immediately see what is happening.
The common solution for this is to sanitize your data at the client level before inserting it into the database. This typically involves replacing any open / closing brackets with their HTML display equivalents (>). When appropriate, stripping out certain key words, such as “script”, or even events (such as “onmouseover” or “onclick”) will prevent these attacks from occurring. There are numerous libraries out there now (both client side and server side) that help with this task.
It is important to remember to sanitize your data coming out from the database as well as sanitizing the data that is coming in. While you may be sanitizing your input adequately before submission, if your database was attacked and data was modified from a different vector (API, direct login, etc.) you could just as easily be exposing your users to danger. Sanitizing your data before you render it on the page will help with this. Browsers are getting smarter about protecting users from XSS, but keeping your data clean is still the best route.
Keeping Sensitive Information in Cloud Source Control
As online code repositories and continuous integration become more popular, so has the problem with stolen credentials and hacked resources. The problem for this is due to the fact that a lot of continuous integration solutions require login or database credentials stored within the code itself in order to function correctly. If your online code repository is out in the public (or worse, hacked) these credentials become free passes to use your resources, wipe out your database, and much more. If you have API keys or other sensitive items there, they could be exploited as well.
The simplest solution is to make sure you’re not using a public cloud repository to store your code. Most providers, such as Bitbucket and GitHub, offer private repositories which lock up your file transfers over a secure connection and are not visible unless you are logged in and have privileges to them. This prevents “drive by hacking” of your credentials.
The best solution is to not have your sensitive information in the code at all. Most cloud hosting platforms, notably Azure and AWS, provide the ability to store your sensitive information in the application hosting container itself, and then inject it into the appropriate configuration file for use. They follow common configuration patterns, so there are only slight code changes that need to be made for this to work. In addition to this, most continuous integration solutions now connect to remote repositories using OAuth or SSH key connections, eliminating the need to have login credentials in your code. These tokens/keys are tied to the source they were originally requested from, so even if they were somehow stolen they couldn’t be used to gain access to your code.
Making sure your code doesn’t fall into these common pitfalls may take additional work up front, but the security they provide will keep your website protected and stable for much longer. Now get out there and code!