CA2100 : Rechercher des failles de sécurité dans des requêtes SQL
TypeName |
ReviewSqlQueriesForSecurityVulnerabilities |
CheckId |
CA2100 |
Catégorie |
Microsoft.Security |
Modification avec rupture |
Modification sans rupture |
Cause
Une méthode définit la propriété IDbCommand.CommandText en utilisant une chaîne construite à partir d'un argument de chaîne de la méthode.
Description de la règle
Cette règle suppose que l'argument de chaîne contient des entrées d'utilisateur.Une chaîne de commande SQL générée par une entrée d'utilisateur est vulnérable aux attaques par injection de code SQL.Dans une attaque d'injection SQL, un utilisateur malveillant fournit des entrées qui modifient le design d'une requête afin d'endommager ou de bénéficier d'un accès non autorisé à la base de données sous-jacente.Les techniques classiques englobent l'injection d'un guillemet simple ou d'une apostrophe qui est le séparateur de chaîne littérale SQL ; de deux tirets qui signifient un commentaire SQL ; et d'un point-virgule qui indique qu'une nouvelle commande suit.Si les entrées d'utilisateur doivent faire partie de la requête, utilisez l'une des méthodes suivantes, répertoriées par ordre d'efficacité, pour réduire le risque d'attaque.
Utilisez une procédure stockée.
Utilisez une chaîne de commande paramétrée.
Validez les entrées d'utilisateur pour le type et le contenu avant de générer la chaîne de commande.
Les types suivants du .NET Framework implémentent la propriété CommandText ou fournissent des constructeurs qui définissent la propriété à l'aide d'un argument de chaîne.
System.Data.Odbc.OdbcCommand et System.Data.Odbc.OdbcDataAdapter
System.Data.OleDb.OleDbCommand et System.Data.OleDb.OleDbDataAdapter
System.Data.OracleClient.OracleCommand et System.Data.OracleClient.OracleDataAdapter
[System.Data.SqlServerCe.SqlCeCommand] et [System.Data.SqlServerCe.SqlCeDataAdapter]
System.Data.SqlClient.SqlCommand et System.Data.SqlClient.SqlDataAdapter
Notez que cette règle n'est pas respectée lorsque la méthode ToString d'un type est utilisée explicitement ou implicitement pour construire la chaîne de requête.Voici un exemple :
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
La règle n'est pas respectée car un utilisateur malveillant peut substituer la méthode ToString().
La règle n'est pas respectée également lorsque la méthode ToString est utilisée implicitement.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Comment corriger les violations
Pour corriger une violation de cette règle, utilisez une requête paramétrée.
Quand supprimer les avertissements
Il est possible de supprimer sans risque un avertissement de cette règle si le texte de commande ne contient pas d'entrée d'utilisateur.
Exemple
L'exemple suivant présente une méthode, UnsafeQuery, qui viole la règle et une méthode, SaferQuery, qui satisfait à la règle en utilisant une chaîne de commande paramétrée.
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'
}