CS.SV.SQL_QUERY
Review SQL queries for security vulnerabilities.
This rule assumes that the string argument contains user input. A SQL command string that is built from user input is vulnerable to SQL injection attacks. In a SQL injection attack, a malicious user supplies input that alters the design of a query in an attempt to damage or gain unauthorized access to the underlying database.
Typical techniques include injection of a single quotation mark or apostrophe, which is the SQL literal string delimiter; two dashes, which signifies a SQL comment; and a semicolon, which indicates that a new command follows. If user input must be part of the query, use one of the following, listed in order of effectiveness, to reduce the risk of attack.
- Use a stored procedure.
- Use a parameterized command string.
- Validate the user input for both type and content before you build the command string.
The following .NET Framework types implement the CommandText property or provide constructors that set the property by using a string argument.
- System.Data.Odbc.OdbcCommand and System.Data.Odbc.OdbcDataAdapter
- System.Data.OleDb.OleDbCommand and System.Data.OleDb.OleDbDataAdapter
- System.Data.OracleClient.OracleCommand and System.Data.OracleClient.OracleDataAdapter
- [System.Data.SqlServerCe.SqlCeCommand] and [System.Data.SqlServerCe.SqlCeDataAdapter]
- System.Data.SqlClient.SqlCommand and System.Data.SqlClient.SqlDataAdapter
Mitigation and prevention
To fix a violation of this rule, use a parameterized query.
Vulnerable code example
using System;
using System.Data;
using System.Data.SqlClient;
namespace SecurityLibrary
{
public class SqlQueries
{
public object UnsafeQuery(
string connection, string name, string password)
{
SqlConnection someConnection = new SqlConnection(connection);
SqlCommand someCommand = new SqlCommand();
someCommand.Connection = someConnection;
someCommand.CommandText = "SELECT AccountNumber FROM Users " +
"WHERE Username='" + name +
"' AND Password='" + password + "'";
someConnection.Open();
object accountNumber = someCommand.ExecuteScalar();
someConnection.Close();
return accountNumber;
}
public object SaferQuery(
string connection, string name, string password)
{
SqlConnection someConnection = new SqlConnection(connection);
SqlCommand someCommand = new SqlCommand();
someCommand.Connection = someConnection;
someCommand.Parameters.Add(
"@username", SqlDbType.NChar).Value = name;
someCommand.Parameters.Add(
"@password", SqlDbType.NChar).Value = password;
someCommand.CommandText = "SELECT AccountNumber FROM Users " +
"WHERE Username=@username AND Password=@password";
someConnection.Open();
object accountNumber = someCommand.ExecuteScalar();
someConnection.Close();
return accountNumber;
}
}
class MalaciousCode
{
static void Main(string[] args)
{
SqlQueries queries = new SqlQueries();
queries.UnsafeQuery(args[0], "' OR 1=1 --", "anything");
// Resultant query (which is always true):
// SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries.SaferQuery(args[0], "' OR 1 = 1 --", "anything");
// Resultant query (notice the additional single quote character):
// SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
// AND Password='anything'
}
}
}
The example shows a method, UnsafeQuery, that violates the rule and a method, SaferQuery, that satisfies the rule by using a parameterized command string.