CA2100: SQL-Abfragen auf Sicherheitsrisiken überprüfen
TypeName |
ReviewSqlQueriesForSecurityVulnerabilities |
CheckId |
CA2100 |
Kategorie (Category) |
Microsoft.Security |
Unterbrechende Änderung |
Nicht unterbrechend |
Ursache
Eine Methode legt die IDbCommand.CommandText-Eigenschaft mithilfe einer Zeichenfolge fest, die aus einem Zeichenfolgenargument für die Methode erstellt wird.
Regelbeschreibung
Diese Regel setzt voraus, dass das Zeichenfolgenargument Benutzereingaben enthält.Eine aus Benutzereingaben erstellte SQL-Befehlszeichenfolge ist anfällig für SQL-Injection-Angriffe.Bei einem SQL-Injection-Angriff stellt ein böswilliger Benutzer Eingaben bereit, die das Design der Abfrage so ändern, dass die zugrunde liegende Datenbank beschädigt wird oder der Benutzer unbefugten Zugriff auf die zugrunde liegende Datenbank erhält.Zu den typischen Techniken zählen das Einfügen eines einfachen Anführungszeichens oder eines Apostrophs, das als Trennzeichen für ein SQL-Zeichenfolgenliteral interpretiert wird, das Einfügen von zwei Bindestrichen, die einen SQL-Kommentar kennzeichnen, sowie das Einfügen eines Semikolons, wodurch angezeigt wird, dass ein neuer Befehl folgt.Falls die Benutzereingaben Teil der Abfrage sein müssen, sollten Sie eine der im Folgenden nach ihrer Effektivität aufgelisteten Methoden anwenden, um das Risiko eines Angriffs zu verringern.
Verwenden Sie eine gespeicherte Prozedur.
Verwenden Sie eine parametrisierte Befehlszeichenfolge.
Überprüfen Sie die Benutzereingaben vor dem Erstellen der Befehlszeichenfolge sowohl auf Typ als auch auf Inhalt.
Die folgenden .NET Framework-Typen implementieren die CommandText-Eigenschaft oder bieten Konstruktoren, die die Eigenschaften mithilfe eines Zeichenfolgenarguments festlegen.
Beachten Sie, dass diese Regel verletzt wird, wenn die ToString-Methode eines Typs explizit oder implizit zum Erstellen der Abfragezeichenfolge verwendet wird.Im Folgenden finden Sie ein Beispiel.
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
Die Regel wird verletzt, da ein böswilliger Benutzer die ToString()-Methode überschreiben kann.
Die Regel wird auch verletzt, wenn ToString implizit verwendet wird.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Behandeln von Verstößen
Um einen Verstoß gegen diese Regel zu korrigieren, verwenden Sie eine parametrisierte Abfrage.
Wann sollten Warnungen unterdrückt werden?
Eine Warnung dieser Regel kann gefahrlos unterdrückt werden, wenn der Befehlstext keine Benutzereingaben enthält.
Beispiel
Im folgenden Beispiel wird eine Methode, UnsafeQuery, gezeigt, die gegen die Regel verstößt. Außerdem wird eine Methode, SaferQuery gezeigt, die die Regel mithilfe einer parametrisierten Befehlszeichenfolge einhält.
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'
}