Combating SQL Injection
SQL injection is preventable. Here's how.
Join the DZone community and get the full member experience.Join For Free
SQL injection is a common form of data theft. Yet, I am hopeful we can make SQL injection protection more common.
The 2018 TrustWave Global Security Report listed SQL injection as the second most common technique for web attacks, trailing only cross-site scripting (XSS) attacks. This is a 38 percent increase from the previous year. That same report also shows SQL Injection ranked fifth on a list of vulnerabilities that can be identified through simple penetration testing.
You may look at the increase and think “whoa, attacks are increasing.” But I believe that what we are seeing is a rising awareness of security. No longer the stepchild, security is a first-class citizen in application design and deployment today. As companies focus on security, they deploy tools and systems to help identify exploits, leading to more reporting of attacks.
SQL injection is preventable. That’s the purpose of this post today — to help you understand what SQL injection is, how to identify when it is happening, and how to prevent it from being an issue.
SQL Injection Explained
An SQL injection is the method where an adversary appends a SQL statement to the input field inside a web page or application, thereby sending their own custom request to a database. That request could be to read data, or download the entire database, or even delete all data completely.
The most common example of SQL injection attacks is found inside username and password input boxes on a webpage. This login design is standard for allowing users to access a website. Unfortunately, many websites do not take precautions to block SQL injection on these input fields, leading to SQL injection attacks.
Let’s look at a sample website built for the fictional Contoso Clinic. The source code for this can be found at https://github.com/Microsoft/azure-sql-security-sample.
On the Patients page, you will find an input field at the top, next to a ‘Search’ button, and next to that a hyperlink for ‘SQLi Hints’.
Clicking on the SQLi Hints link will display some sample text to put into the search field.
I’m going to take the first statement and put it into the search field. Here is the result:
This is a common attack vector, as the adversary can use this method to determine what version of SQL Server is running. This is also a nice reminder to not allow your website to return such error details to the end user. But more on that later.
Let’s talk a bit about how SQL injection works under the covers.
How SQL Injection Works
The vulnerability in my sample website is the result of this piece of code:
return View(db.Patients.SqlQuery ("SELECT * FROM dbo.Patients WHERE [FirstName] LIKE '%" + search + "%' OR [LastName] LIKE '%" + search + "%' OR [StreetAddress] LIKE '%" + search + "%' OR [City] LIKE '%" + search + "%' OR [State] LIKE '%" + search + "%'").ToList());
This is a common piece of code used by many websites. It is building a dynamic SQL statement based upon the input fields on the page. If I were to search the Patients page for ‘Rock,’ the SQL statement sent to the database would then become:
SELECT * FROM dbo.Patients WHERE [FirstName] LIKE '%Rock%' OR [LastName] LIKE '%Rock%' OR [StreetAddress] LIKE '%Rock%' OR [City] LIKE '%Rock%' OR [State] LIKE '%Rock%'
In the list of SQLi hints on that page, you will notice that each example starts with a single quote, followed by a SQL statement, and at the end, is a comment block (the two dashes). For the example that I chose above, the resulting statement is as follows:
SELECT * FROM dbo.Patients WHERE [FirstName] LIKE '%' OR CAST(@@version as int) = 1 --%' OR [LastName] LIKE '%' OR CAST(@@version as int) = 1 --%' OR [StreetAddress] LIKE '%' OR CAST(@@version as int) = 1 --%' OR [City] LIKE '%' OR CAST(@@version as int) = 1 --%' OR [State] LIKE '%' OR CAST(@@version as int) = 1 --%'
This results in the conversion error shown above. This also means that I can do interesting searches to return information about the database. Or, I could do malicious things, like drop tables.
Chance are, you have code like this, somewhere, right now. Let’s look at how to find out what your current code looks like.
SQL Injection Discovery
Discovering SQL injection is not trivial. You must examine your code to determine if it is vulnerable. You must also know if someone is actively trying SQL injection attacks against your website. Trying to roll your own solution can take considerable time and effort.
There are two tools I can recommend you use to help discover SQL injection.
Test Websites With SQLMap
One method is to use sqlmap, an open-source penetration testing project that will test websites for SQL injection vulnerabilities. This is a great way to uncover vulnerabilities in your code. However, sqlmap will not tell you if someone is actively using SQL injection against your website. You will need to use something else for alerts.
Azure Threat Detection
If you are using Azure SQL Database, then you have the option to enable Azure Threat Detection. This feature will discover code vulnerabilities as well as alert you to attacks. It also checks for anomalous client login, data exfiltration, and if a harmful application is trying to access your database.
(For fairness, I should mention that AWS WAF allows for SQL injection detection, but their process is a bit more manual than Azure).
If you try to roll your own discovery, you will want to focus on finding queries that have caused errors. Syntax errors, missing objects, permission errors, and UNION ALL errors are the most common. You can find a list of the common SQL Server error message numbers here.
It warrants mentioning that not all SQL injection attacks are discoverable. But when it comes to security, you will never eliminate all risk, you take steps to lower your risk. SQL injection discovery is one way to lower your risk.
SQL Injection Protection
Detection of SQL injection vulnerabilities and attacks are only part of the solution. In an ideal world, your application code would not allow for SQL injection. Here’s a handful of ways you can lower your risk of SQL injection attacks.
Parameterize Your Queries
Also known as ‘prepared statements,’ this is a good way to prevent SQL injection attacks against the database. For SQL Server, prepared statements are typically done using the sp_executesql() system stored procedure.
Prepared statements should not allow an attacker to change the nature of the SQL statement by injecting additional code into the input field. I said “should”, because it is possible to write prepared statements in a way that would still be vulnerable to SQL injection. You must (1) know what you are doing and (2) learn to sanitize your inputs.
Traditionally, one argument against the use of prepared statements centers on performance. It is possible that a prepared statement may not perform as well as the original dynamic SQL statement. However, if you are reading this and believe performance is more important than security, you should reconsider your career in IT before someone does that for you.
Use Stored Procedures
Another method available is stored procedures. Stored procedures offer additional layers of security that prepared statements may not allow. While prepared statements require permissions on the underlying tables, stored procedures can execute against objects without the user having similar direct access.
Like prepared statements, stored procedures are not exempt from SQL injection. It is quite possible you could put vulnerable code into a stored procedure. You must take care to compose your stored procedures properly, making use of parameters. You should also consider validating the input parameters being passed to the procedure, either on the client side or in the procedure itself.
Use EXECUTE AS
You could use a security method such as EXECUTE AS to switch the context of the user as you make a request to the database. As mentioned above, stored procedures somewhat act in this manner by default. But EXECUTE AS can be used directly for requests such as prepared statements or ad-hoc queries.
Remove Extended Stored Procedures
Disabling the use of extended stored procedures is a good way to limit your risk with SQL injection. Not because you won’t be vulnerable, but because you limit the surface area for the attacker. By disabling these system procedures, you limit a common way that an attacker can get details about your database system.
Sanitize Error Messages
You should never reveal error messages to the end user. Trap all errors and redirect to a log for review later. The less error information you bubble up, the better.
Whitelisting of IP addresses is a good way to limit activity from anomalous users. Use of VPNs and VNETs to segment traffic can also reduce your risk.
The hard truth here is that every database is susceptible to SQL injection attacks. No one platform is more at risk than any other. The weak link here is the code written on top of the database. Most code development does not emphasize security enough, leaving themselves open to attacks.
When you combine poor database security techniques along with poor code, you get the recipe for SQL Injection.
- 2018 TrustWave Global Security Report
- Contoso Clinic Demo Application
- sqlmap: Automatic SQL injection and database takeover tool
- Azure SQL Database threat detection
- Working with SQL Injection Match Conditions
- How to Detect SQL Injection Attacks
- sp_executesql (Transact-SQL)
- EXECUTE AS (Transact-SQL)
- Server Configuration Options (SQL Server)
Published at DZone with permission of Thomas LaRock, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.