Reviewing Code for SQL Injection

OWASP Code Review Guide Table of Contents

Contact author: [mailto:eoinkeary@owasp.org Eoin Keary]

What SQL injection is:
SQL injection is a security vulnerability that occurs in the persistence/database layer of a web application. This vulnerability is derived from the incorrect escaping of variables embedded in SQL statements. It is in fact an instance of a more general class of vulnerabilities based on poor input validation and bad design that can occur whenever one programming or scripting language is embedded inside another.

How to Locate potentially vulnerable code
A secure way to build SQL statements is to construct all queries with PreparedStatement instead of Statement and/or to use parameterized stored procedures. Parameterized stored procedures are compiled before user input is added, making it impossible for a hacker to modify the actual SQL statement.

The account used to make the database connection must have “Least privilege” If the application only requires read access then the account must be given read access only.

Avoid disclosing error information: Weak error handling is a great way for an attacker to profile SQL injection attacks. Uncaught SQL errors normally give too much information to the user and contain things like table names and procedure names.

Best practices when dealing with DB’s
Use Database stored procedures, but even stored procedures can be vulnerable. Use parametrized queries instead of dynamic SQL statements. Data validate all external input: Ensure that all SQL statements recognize user inputs as variables, and that statements are precompiled before the actual inputs are substituted for the variables in Java.

SQL Injection Example:
String DRIVER = "com.ora.jdbc.Driver";

String DataURL = "jdbc:db://localhost:5112/users";

String LOGIN = "admin";

String PASSWORD = "admin123";

Class.forName(DRIVER);

//Make connection to DB Connection connection = DriverManager.getConnection(DataURL, LOGIN, PASSWORD);

String Username = request.getParameter("USER"); // From HTTP request

String Password = request.getParameter("PASSWORD"); // From HTTP request

int iUserID = -1;

String sLoggedUser = "";

String sel = "SELECT User_id, Username FROM USERS WHERE Username = '" +Username + "' AND Password = '" + Password + "'";

Statement selectStatement = connection.createStatement ; ResultSet resultSet = selectStatement.executeQuery(sel);

if (resultSet.next) {

iUserID = resultSet.getInt(1); sLoggedUser = resultSet.getString(2); }

PrintWriter writer = response.getWriter ;

if (iUserID >= 0) { writer.println ("User logged in: " + sLoggedUser); } else {

writer.println ("Access Denied!") }

When SQL statements are dynamically created as software executes, there is an opportunity for a security breach as the input data can truncate or malform or even expand the original SQL query!

Firstly the request.getParameter retrieves the data for the SQL query directly from the HTTP request without any Data validation (Min/Max length, Permitted characters, malicious characters). This error gives rise to the ability to input SQL as the payload and alter the functionality in the statement.

The application places the payload directly into the statement causing the SQL vulnerability:

String sel = "SELECT User_id, Username FROM USERS WHERE Username = '" Username + "' AND Password = '" + Password + "'";

.NET
Parameter collections such as SqlParameterCollection provide type checking and length validation. If you use a parameters collection, input is treated as a literal value, and SQL Server does not treat it as executable code and therefore the payload can not be injected. Using a parameters collection lets you enforce type and length checks. Values outside of the range trigger an exception. Make sure you handle the exception correctly. Example of the SqlParameterCollection:

using System.Data;

using System.Data.SqlClient;

using (SqlConnection conn = new SqlConnection(connectionString)) { DataSet dataObj = new DataSet;

SqlDataAdapter sqlAdapter = new SqlDataAdapter( "StoredProc", conn);

sqlAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;

//specify param type

sqlAdapter.SelectCommand.Parameters.Add("@usrId", SqlDbType.VarChar, 15);

sqlAdapter.SelectCommand.Parameters["@usrId "].Value = UID.Text; // Add data from user

sqlAdapter.Fill(dataObj); // populate and execute proc

}

Stored procedures don’t always protect against SQL injection:

CREATE PROCEDURE dbo.RunAnyQuery @parameter NVARCHAR(50)

AS EXEC sp_executesql @parameter GO

The above procedure shall execute any SQL you pass to it. The directive sp_executesql is a system stored procedure in Microsoft® SQL Server™

Lets pass it.

DROP TABLE ORDERS;

Guess what happens? So we must be careful of not falling into the “We’re secure, we are using stored procedures” trap!