Richtlinien für das Schreiben von sicherem ADO.NET-Code
Das Sichern von Anwendungen umfasst das Schreiben von sicherem Code. Code darf lediglich Informationen und Funktionen offen legen, die vom Clientcode benötigt werden. Übliche Angriffe in Verbindung mit ADO.NET sind SQL-Einfügeangriffe und das Ermitteln geheimer Datenbankinformationen unter Ausnutzung der von der Anwendung zurückgegebenen Ausnahmen.
Vermeiden von SQL-Einfügeangriffen
Bei einem SQL-Einfügeangriff fügt ein Angreifer zusätzliche SQL-Anweisungen in Befehle ein, die in der Datenquelle verarbeitet werden. Mit diesen Befehlen können vertrauliche Informationen abgerufen sowie in der Datenquelle enthaltene Informationen geändert bzw. zerstört werden. Code ist für einen SQL-Einfügeangriff anfällig, wenn Befehlszeichenfolgen mit externen Eingaben verkettet werden. Der folgende Code ist beispielsweise anfällig für SQL-Einfügeangriffe.
' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()
' The following line of code allows for SQL Insertion attack.
Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = " & custID
Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();
// The following line of code allows for SQL Insertion attack.
string selectString = "SELECT * FROM Customers WHERE CustomerID = " + custID;
SqlCommand cmd = new SqlCommand(selectString, conn);
conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();
Ein Angreifer kann z. B. den Wert "1;DROP TABLE Customers" für die abzufragende CustomerID eingeben. In diesem Fall wird der folgende Befehl für die Abfrage ausgeführt.
SELECT * FROM Customers WHERE CustomerID = 1;DROP TABLE Customers
Sie können sich gegen SQL-Einfügeangriffe schützen, indem Sie Eingaben aus externen Quellen überprüfen und Spaltenwerte als Parameter weiterleiten, anstatt Werte zum Erstellen einer SQL-Anweisung zu verketten.
Überprüfen der Eingabe
Wenn Sie die Übereinstimmung der Eingabe mit einem bestimmten Format überprüfen möchten, können Sie reguläre Ausdrücke verwenden. .NET Framework stellt das Regex-Objekt zum Überprüfen eines Wertes mit einem regulären Ausdruck zur Verfügung. Mit dem folgenden Code kann z. B. sichergestellt werden, dass der Wert für eine Benutzer-ID aus einer alphanumerischen Zeichenfolge mit fünf Zeichen besteht.
Public Static Function Validate(inString As String) As Boolean
Dim r As Regex = New Regex("^[A-Za-z0-9]{5}$")
Return r.IsMatch(inString)
End Function
[C#]
public static bool Validate(string inString)
{
Regex r = new Regex("^[A-Za-z0-9]{5}$");
return r.IsMatch(inString)
}
Verwenden von Parametern
Parameter stellen eine geeignete Methode zum Organisieren von Werten dar, die mit einer SQL-Anweisung weitergeleitet bzw. an eine gespeicherte Prozedur übergeben werden. Darüber hinaus bietet die Verwendung von Parametern Schutz gegen einen SQL-Einfügeangriff, da sichergestellt ist, dass Werte aus einer externen Quelle lediglich als Werte und nicht als Bestandteil einer SQL-Anweisung weitergeleitet werden. In einen Wert eingefügte SQL-Befehle werden daher nicht an der Datenquelle ausgeführt. Die weitergeleiteten Werte werden stattdessen nur als Parameterwert verarbeitet. Das folgende Codebeispiel veranschaulicht die Verwendung eines Parameters zum Weiterleiten eines Wertes.
' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()
Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = @CustomerID"
Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID
conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();
string selectString = "SELECT * FROM Customers WHERE CustomerID = @CustomerID";
SqlCommand cmd = new SqlCommand(selectString, conn);
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID;
conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();
Schützen von Ausnahmeinformationen
Für eine bestimmte Art von Angriffen auf ein System verwenden Angreifer häufig Informationen aus einer Ausnahme, z. B. den Namen eines Servers, einer Datenbank oder Tabelle. Da Ausnahmen spezifische Informationen über eine Anwendung bzw. Datenquelle enthalten können, können Sie Ihre Anwendung bzw. Datenquelle besser schützen, indem Sie lediglich Informationen zur Verfügung stellen, die vom Client explizit angefordert werden.
Vermeiden Sie das Offenlegen privater Informationen durch Ausnahmen, indem Sie den Inhalt von Systemausnahmen nicht an Benutzer zurückgeben. Verarbeiten Sie die Ausnahme stattdessen intern. Verwenden Sie für erforderliche Nachrichten an Benutzer eigene benutzerdefinierte Meldungen mit minimalen Informationen (z. B. "Die Verbindung ist fehlgeschlagen. Wenden Sie sich an den Systemadministrator."), und protokollieren Sie die jeweiligen Informationen für den Administrator.
Mit dem folgenden Beispielcode werden Ausnahmen beim Öffnen einer Verbindung ermittelt und in das Ereignisprotokoll geschrieben.
Dim conn As SqlConnection = New SqlConnection("Data Source=localhost;Initial Catalog=Northwind;")
Try
conn.Open()
Catch e As SqlException
Dim log As System.Diagnostics.EventLog = New System.Diagnostics.EventLog()
log.Source = "My Application"
log.WriteEntry(e.ToString())
If conn.State <> ConnectionState.Open Then _
Console.WriteLine("Connection was not opened.")
Finally
conn.Close()
End Try
[C#]
SqlConnection conn = new SqlConnection("Data Source=localhost;Initial Catalog=Northwind;");
try
{
conn.Open();
}
catch (SqlException e)
{
System.Diagnostics.EventLog log = new System.Diagnostics.EventLog();
log.Source = "My Application";
log.WriteEntry(e.ToString());
if (conn.State != ConnectionState.Open)
Console.WriteLine("Connection was not opened.");
}
finally
{
conn.Close();
}