OWASP Top 10 Risks: #1: Injection
OWASP Top 10 Risks: #1: Injection
Join the DZone community and get the full member experience.Join For Free
Read why times series is the fastest growing database category.
We all have probably witnessed (and had a good laugh) when commands have been mixed with data as in the case where the baker literally writes the instructions for the message as the cake message. There isn't much difference in this example and what is one of the core elements in what OWASP has identified as the no. 1 application security risk to organizations; the risk of an injection. Before getting into what comprises an injection, how it is carried out and what defense measure we can take, let me introduce you to OWASP by deferring to their own description:
The Open Web Application Security Project (OWASP) is a 501(c)(3) worldwide not-for-profit charitable organization focused on improving the security of software. Our mission is to make software security visible, so that individuals and organizations worldwide can make informed decisions about true software security risks.
OWASP’s Top 10
For a number of years now, OWASP have been publishing a list of the Top 10 Application Security Risks for developers to use to be more responsible with their applications. The words “responsible” and “software developer” are not words you hear together to often. But in the day of online banking accounts, personal profiles and online shopping, software developers should be taking a more responsible approach to their craft. One way to demonstrate responsibility is being very well versed in the common security risks that online applications face. A way to achieve that is through the familiarity of the risks that have been identified in OWASP’s Top 10 list and the information they provide for identifying and recommended countermeasures.
From the previous 2010 Top 10 list to the current 2013, not much has changed with the exception of a few risks reordering based on severity. However, the topic of this post has remained the #1 risk on both editions.
Injection You Say?
The term injection can encompass a large number of different variations such as SQL, XML, LDAP, HTML, CSS and Remote file injections (which is not an exhaustive list). In the end, they all boil down to injecting a command in the guise of data, where the command is then executed with malicious results on the targeted system. This is all a result of what OWASP has identified as Injection Flaws which they have defined as:
Injection flaws, such as SQL, OS, and LDAP injection occur when distrusted data is sent to an
interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter
into executing unintended commands or accessing data without proper authorization.
Lets look at some examples…
Injection in Action
No reasonable documentation on injection can be complete without the conventional SQL injection examples. SQL Injections are one of, if not the most prevalent type of injection due to their simplicity and technology prevalence and can easily be demonstrated.
To set the stage, imagine for a moment you have a public API exposed http://mycornyproducts.com/api/Products that will provide a consumer of your API to retrieve product information. Behind the scenes you take that input (data) which will formulate as the criteria for the query you run against your applications database.
A SQL injection example at its core is made up of 2 ingredients; query and data. In our example we have the following query structure:SELECT * FROM Products WHERE Name = Data
In this case, Name is a string data type and Data can represent any valid criteria, for example “Flash Light” and return a record:
Here we see Data represented as it was intended. But remember, this is user data, distrusted and invalidated user data. Recall the earlier statement about commands becoming data? What if the previous submitted user data looked something like this:
Where bat; update products set name = ‘All your base are belong to us’ is the provided data by the user. We can see what the query would end up looking like without any validation of the use input:SELECT * FROM Products WHERE Name =
‘bat’; update products set name = ‘All your base are belong to us’
We can see here that our criteria Data has now become a Command of updating all product names and when executed against our database table would have the following results:
This is a pretty straight forward example with minimal impact. But this example could be in the form of any number of scenarios such as inserting a user with the keys to the kingdom or forcing the leakage of sensitive data through additional sub queries and the list goes on. Now having seen an example of an injection in action, lets look at the root cause for such a vulnerability so we can formulate a plan of defense.
Understanding the Root Problem
Before diving into what can be done to stop this type of vulnerability, lets focus on the root problem for a moment. In most cases if not all, the root problem is the allowance of distrusted data into our system. Depending on the type of injection, distrusted data can access a system from a number of different sources. An incomplete list of sources where distrusted data can garnish access to your system are:
- Request Headers
- Form fields
- Query strings
- Isolated storage
- Image Exif
In all these cases, our system doesn't have immediate and complete control over them to ensure they can be a trusted source. Disclaimer: there are probably cases where trusted sources of a system can still be the catalyst for distrusted and malicious data.
The distrusted data we have been mentioning is some form of a command that has come in as a guise of valid data. Lets look at how we can protect our system from injection vulnerability.
Your Maginot Line (Defense in Depth)
If your not familiar with the French Maginot Line, the French established a defense in depth line of fortifications, turrets, guns and traps (just to name a few) on their border with Germany which was successful in withstanding a direct attack. We also are going to take a defense in depth approach to protecting out system by implementing a 3 layer defense approach (each defense being a mitigation). It’s important to point out that the Maginot Line had a weakness and your Maginot defense line can too, but we’ll come back to that shortly.
The 3 mitigation we are going to apply are:
- Input Validation
- Query Parameterization
- Principle of Least Privilege
To summarize our approach, we want to impose rules of validation on all distrusted data. In the case that malicious data is incorrectly validated, safe guard our internal queries by encapsulating the input into named statements. Finally, regulate our applications database accounts with the least privileges it needs to perform its job.
If you have used a strongly typed language like C# and Java you have performed implicit user validation whether you knew or not, compliments of the language’s type safety. When your ASP.NET Web API or MVC controller action requires an ID of type integer, you automatically have some level of input validation for free. However, rely on the language’s type safety for input validation is not enough. In many cases type safety won’t even be an option as is the case with dynamic languages or user input in the form of strings; where you truly are left to the creativity of the user as to what input will be submitted to your application. This leaves you with no other option but to perform some explicit validation on the input.
There are generally two validation approaches, validating input against white list (approved patterns) or validating against a blacklist (patterns that have been deemed invalid). There is a big difference between the two and can easily mean the difference of this line of defense being successful.
Blacklists retain all the invalid patterns that you want to safe guard against. For example, validating an email address by ensuring it doesn’t match an entry in your blacklist. The problem with the blacklist approach is maintenance. If there is change or a new pattern you need to safe guard against, you’re vulnerable to that pattern until you update your blacklist. Contrary to what some people might state about blacklist, they are necessary at times. However, white list take the complete opposite approach and are generally looked at as a best practice approach.
White list maintain a list of patterns you deem valid. Therefore, no matter what input is submitted, if it doesn’t adhere to a valid pattern in the white list, validation can’t occur. Now, there has to be some case where the previous sentence is wrong, but that is why input validation is not our only means of defense, remember this is defense in-depth approach. Therefore, if you’re applications requirements is to only accept 3 valid email formats (patterns), you only need to verify that a submitted email adheres to one of the 3 valid patterns to know it is valid. You don’t care about all the other possibilities. In the end, white list allow easier maintenance, easier to read and comprehend and lend to a more secure validation approach.
Depending on the application, multiple tiers of input validation can be setup as in the case of enforcing input validation rules at the client level on all input fields. This is then followed up by server-side validation of all input. If you do leverage a number of existing validation tools such as ASP.NET Web form validators, Data Annotations and/or their jQuery Unobtrusive library (just to name a few), make sure you’re aware of how they operate. Blindly relying on some 3rd party or framework’s validation tools is like trusting your kids to any o’babysitter with no background check.
However, client validation is not always an available luxury as in the case of a REST API or web service where the client is out of your control. Therefore, server-side validation is not an option and should always be performed whether you have control of the client or not. I have said before and will say it again
In some cases, the importance of where you are handling specific security related issues can be as important as whether you are at all.
Remember, in most cases of injection and the ones we have been concentrating on, the bottom line is that we are processing some type of statement against our data source based on the submitted distrusted data. In the case of a RDBMS it could be a query for a record, carry out a command on a record(s) or a combination of both. The problem enters when we are concatenating our distrusted data with the statement.
As in the examples from the previous Injection in Action section
SELECT * FROMProductsWHEREName = distrusted_data
Where distrusted data is its own query and ends up revealing all products public or not
SELECT * FROMProductsWHEREName = ‘bat’or 1=1; - -
Or malicious in a different way such as carrying out its own command
SELECT * FROMProductsWHEREName = ‘bat’; Drop Table Products; - -
In all these examples, the vulnerability comes from the fact that we have simply concatenated our query with the distrusted data. We can plug this vulnerability by letting our database engine know the difference between the statement (query) we want to run and the criteria (distrusted data) that we provide. This allows the database engine to properly escape the criteria and not allow it to operate outside it’s intended purpose.
Forgetting for the moment about ORM’s (object relation mapper) such as Entity Framework lets look at what a parameterized query would look like:
var conn =
"SELECT * FROM Products WHERE name=@Criteria"
(var sqlCommand =
, SqlDbType.NVarChar).Value = name;
var reader = sqlCommand.ExecuteReader();
//removed for brevity
We can see that we clearly defining the query and separately the data. This is done by defining what the query will be and specifying placeholders (@Criteria) of where the data is expected. Followed up by defining what the data’s type (SqlDbType.NVarChar) is and it’s value (name). In the case of more constrained types (i.e. integers) our SQL database engine would throw an error if the supplied data doesn’t conform.
In an example where the submitted input for our name parameter has malicious content such as 1; update products set name = ‘All your base are belong to us’, the above code will render the following parameterized query:
execsp_executesqlN'SELECT * FROM Products WHERE name=@Criteria',N'@Criteria nvarchar(62)',@Criteria=N'1; update products set name = ‘All your base are belong to us’'
The important part of this final result, is that the distrusted data has been escaped and is operated against as data of type NVarChar and no longer a valid SQL statement that will be executed.
Depending on your organization's policies or application performance, utilizing Stored Procedures is yet another way to enforce parameterization. But they provide the same level of security through the same mechanisms of separation of data from query and definition of data type which result in the escaping of the data by the database engine before execution.
Dangers to be Aware of
One might be thinking “I use an ORM, so I already have this covered”. That’s true, most ORM’s such as Entity Framework and NHiberate provide prepared statements as the de facto method of operation. The problem that arises whether you use ORM’s or stored procedures comes when you start dynamically creating statements.
Based on the complexity of your query, you might find yourself taking a shortcut or because of tool limitations by dynamically constructing a string to execute as a query by the native means of your ORM or within the stored procedures themselves. Be aware that when you do this, you have formally put yourself back in seat of the original problem. Which brings us to our next defense.
Principle of Least Privilege
How many times have we heard or stated ourselves, that developers are lazy? The truth couldn't be more apparent with how we devise the database users accounts that our application operate under. More likely than not, most public facing applications you have worked on have only one account that all their database layer operations connect under. In addition, this single account usually retains at minimum both read and write access.
The above scenario introduces the need for applying the Principle of Least Privilege. Which, for us, basically means diversifying the accounts the application uses and enforcing that each account operates under the minimal permissions needed to carry out its objective.
In the examples we have used up until now, where we are simply allowing users to query for products by a certain name, the database account only needs to be able to read from the database.
Alternatively, we can be more restrictive and define exactly what objects and the permissions on those objects that the account has. Each RDBMS is different. For example, below is Microsoft SQL Server restricting this user to SELECT statements on the PRODUCTS table.
What does this do for us? This restriction to the appropriate account with minimal permissions would have stopped our previous examples of input with malicious intent to write to our database. This wouldn’t stop a malicious read query that attempts to leak unintended records or database schema information, but that would be covered under the safe guard of query parameterization.
All this talk about restricting access at the database layer begs the question of managing different database accounts and connections within your application. This obviously introduces additional overhead and complexity but greatly increases the security at the database layer. I do plan on writing a post specifically on known ways to manage multiple accounts, managing connection strings and options available in the very near future. For now, a very simple example would be the separation of administrative and public areas each with their own accounts restricted to the minimal permissions needed.
Making Sense of it All
We have definitely covered a lot here, but the take away is that defense against injection is not composed of a single action but defense in-depth; multiple layers that each provide a particular overlapping protection. Like the French Maginot Line, your defense can be formidable.
But remember when I said that the Maginot Line has a weakness? Well in WWII on May 10th, 1940, instead of taking on the Maginot Line head first, the Germans avoided the Maginot defense by going around and entering France by way of an unprotected area (comparably) north of the Maginot Line and within 43 days had won the Battle of France forcing France to sign an armistice on June 22nd. Making it one of the quickest defeats of what was presumed to be one of, if not the best armies at the time. There is two points to take away from this. If you shortcut an area of your defense in-depth or completely opt out of implementing some layer of defense, you leave an area of your application vulnerable. In addition, we have only discussed one type of risk. Therefore, not being cognitive and taking deliberate preventative steps to protect from other risks will leave your application vulnerable. Stay tuned for up coming posts on additional OWASP Top 10 risks.
OWASP Top 10 Risks: #1: Injection first appeared on lockmedown.com
featured Image via: pleated-jeans.com
Opinions expressed by DZone contributors are their own.