CA2100: Controllare la vulnerabilità della sicurezza nelle query SQL
TypeName |
ReviewSqlQueriesForSecurityVulnerabilities |
CheckId |
CA2100 |
Category |
Microsoft.Security |
Breaking Change |
Non sostanziale |
Causa
Un metodo imposta la proprietà IDbCommand.CommandText utilizzando una stringa compilata da un argomento stringa nel metodo.
Descrizione della regola
La regola presuppone che l'argomento stringa contenga l'input dell'utente. Una stringa di comando SQL compilata da un input dell'utente è vulnerabile agli attacchi intrusivi nel codice SQL, nei quali un utente malintenzionato fornisce un input che modifica la progettazione di una query nel tentativo di danneggiare o di ottenere accesso non autorizzato al database sottostante. Le tecniche tipiche includono l'intrusione di un apice o un apostrofo, che è delimitatore di stringa letterale di SQL, di due trattini, che indicano un commento SQL, e di un punto e virgola, che indica un nuovo comando a seguire. Se l'input dell'utente deve essere parte della query, scegliere una delle soluzioni riportate di seguito ed elencate in ordine di efficacia per ridurre il rischio di attacco.
Utilizzare una stored procedure.
Utilizzare una stringa di comando con parametri.
Convalidare l'input dell'utente sia per tipo che per contenuto prima di compilare la stringa di comando.
I seguenti tipi di .NET Framework implementano la proprietà CommandText o forniscono costruttori che la impostano mediante un argomento stringa.
System.Data.Odbc.OdbcCommand e System.Data.Odbc.OdbcDataAdapter
System.Data.OleDb.OleDbCommand e System.Data.OleDb.OleDbDataAdapter
System.Data.OracleClient.OracleCommand e System.Data.OracleClient.OracleDataAdapter
[System.Data.SqlServerCe.SqlCeCommand] e [System.Data.SqlServerCe.SqlCeDataAdapter]
System.Data.SqlClient.SqlCommand e System.Data.SqlClient.SqlDataAdapter
Si noti che questa regola viene violata quando il metodo ToString di un tipo viene utilizzato in modo esplicito o implicito per costruire la stringa di query. Di seguito è riportato un esempio.
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
La regola viene violata perché un utente malintenzionato può eseguire l'override del metodo ToString().
La regola è violata anche quando viene utilizzato ToString in modo implicito.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Come correggere le violazioni
Per correggere una violazione di questa regola, utilizzare una query con parametri.
Esclusione di avvisi
L'esclusione di un avviso da questa regola è sicura se il testo del comando non contiene alcun input dell'utente.
Esempio
Nell'esempio riportato di seguito vengono illustrati il metodo UnsafeQuery che viola la regola e il metodo SaferQuery che la soddisfa mediante l'utilizzo di una stringa di comando con parametri.
Imports System
Imports System.Data
Imports System.Data.SqlClient
Namespace SecurityLibrary
Public Class SqlQueries
Function UnsafeQuery(connection As String, _
name As String, password As String) As Object
Dim someConnection As New SqlConnection(connection)
Dim someCommand As New SqlCommand()
someCommand.Connection = someConnection
someCommand.CommandText = "SELECT AccountNumber FROM Users " & _
"WHERE Username='" & name & "' AND Password='" & password & "'"
someConnection.Open()
Dim accountNumber As Object = someCommand.ExecuteScalar()
someConnection.Close()
Return accountNumber
End Function
Function SaferQuery(connection As String, _
name As String, password As String) As Object
Dim someConnection As New SqlConnection(connection)
Dim someCommand As 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()
Dim accountNumber As Object = someCommand.ExecuteScalar()
someConnection.Close()
Return accountNumber
End Function
End Class
Class MalaciousCode
Shared Sub Main(args As String())
Dim queries As 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'
End Sub
End Class
End Namespace
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'
}
}
}
#using <System.dll>
#using <System.Data.dll>
#using <System.EnterpriseServices.dll>
#using <System.Transactions.dll>
#using <System.Xml.dll>
using namespace System;
using namespace System::Data;
using namespace System::Data::SqlClient;
namespace SecurityLibrary
{
public ref class SqlQueries
{
public:
Object^ UnsafeQuery(
String^ connection, String^ name, String^ password)
{
SqlConnection^ someConnection = gcnew SqlConnection(connection);
SqlCommand^ someCommand = gcnew SqlCommand();
someCommand->Connection = someConnection;
someCommand->CommandText = String::Concat(
"SELECT AccountNumber FROM Users WHERE Username='",
name, "' AND Password='", password, "'");
someConnection->Open();
Object^ accountNumber = someCommand->ExecuteScalar();
someConnection->Close();
return accountNumber;
}
Object^ SaferQuery(
String^ connection, String^ name, String^ password)
{
SqlConnection^ someConnection = gcnew SqlConnection(connection);
SqlCommand^ someCommand = gcnew 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;
}
};
}
using namespace SecurityLibrary;
void main()
{
SqlQueries^ queries = gcnew SqlQueries();
queries->UnsafeQuery(Environment::GetCommandLineArgs()[1],
"' OR 1=1 --", "anything");
// Resultant query (which is always true):
// SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries->SaferQuery(Environment::GetCommandLineArgs()[1],
"' OR 1 = 1 --", "anything");
// Resultant query (notice the additional single quote character):
// SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
// AND Password='anything'
}