Wir haben hier eine weitere Diskussion über die Verwendung parametrisierter SQL-Abfragen in unserem Code. Wir haben zwei Seiten in der Diskussion: Ich und einige andere, die sagen, wir sollten immer Parameter verwenden, um uns vor SQL-Injektionen zu schützen, und die anderen, die es nicht für notwendig halten. Stattdessen möchten sie einzelne Apostrophe durch zwei Apostrophe in allen Zeichenfolgen ersetzen, um SQL-Injektionen zu vermeiden. Auf unseren Datenbanken wird Sql Server 2005 oder 2008 ausgeführt, und auf unserer Codebasis wird .NET Framework 2.0 ausgeführt.
Lassen Sie mich Ihnen ein einfaches Beispiel in C # geben:
Ich möchte, dass wir dies verwenden:
string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe
Während die anderen das machen wollen:
string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?
Wo die SafeDBString-Funktion wie folgt definiert ist:
string SafeDBString(string inputValue)
{
return "'" + inputValue.Replace("'", "''") + "'";
}
Solange wir SafeDBString für alle Zeichenfolgenwerte in unseren Abfragen verwenden, sollten wir jetzt sicher sein. Richtig?
Es gibt zwei Gründe, die SafeDBString-Funktion zu verwenden. Erstens ist es so, wie es seit der Steinzeit gemacht wurde, und zweitens ist es einfacher, die SQL-Anweisungen zu debuggen, da Sie die exakte Abfrage sehen, die in der Datenbank ausgeführt wird.
Also dann. Meine Frage ist, ob es wirklich ausreicht, die SafeDBString-Funktion zu verwenden, um SQL-Injection-Angriffe zu vermeiden. Ich habe versucht, Beispiele für Code zu finden, der diese Sicherheitsmaßnahme verletzt, aber ich kann keine Beispiele dafür finden.
Gibt es da draußen jemanden, der das brechen kann? Wie würdest du es machen?
EDIT: Um die bisherigen Antworten zusammenzufassen:
- Bisher hat noch niemand einen Weg gefunden, um SafeDBString auf SQL Server 2005 oder 2008 zu umgehen. Das ist gut, denke ich?
- In mehreren Antworten wurde darauf hingewiesen, dass Sie bei Verwendung parametrisierter Abfragen einen Leistungsgewinn erzielen. Der Grund ist, dass die Abfragepläne wiederverwendet werden können.
- Wir sind uns auch einig, dass die Verwendung parametrisierter Abfragen besser lesbaren Code liefert, der einfacher zu warten ist
- Außerdem ist es einfacher, immer Parameter zu verwenden, als verschiedene Versionen von SafeDBString, Konvertierungen von Zeichenfolgen in Zahlen und Konvertierungen von Zeichenfolgen in Datumsangaben zu verwenden.
- Wenn Sie Parameter verwenden, erhalten Sie eine automatische Typkonvertierung. Dies ist besonders nützlich, wenn Sie mit Datums- oder Dezimalzahlen arbeiten.
- Und schließlich: Versuchen Sie nicht, selbst Sicherheit zu leisten, wie JulianR schrieb. Die Datenbankanbieter investieren viel Zeit und Geld in die Sicherheit. Es gibt keine Möglichkeit, es besser zu machen, und keinen Grund, warum wir versuchen sollten, ihre Arbeit zu erledigen.
Obwohl niemand in der Lage war, die einfache Sicherheit der SafeDBString-Funktion zu beeinträchtigen, habe ich viele andere gute Argumente erhalten. Vielen Dank!
quelle
Antworten:
Ich denke die richtige Antwort ist:
Versuchen Sie nicht, selbst Sicherheit zu leisten . Verwenden Sie eine beliebige vertrauenswürdige Bibliothek nach Industriestandard, die für das verfügbar ist, was Sie tun möchten, anstatt es zu versuchen es selbst zu . Unabhängig davon, welche Annahmen Sie zur Sicherheit treffen, sind diese möglicherweise falsch. So sicher Ihr eigener Ansatz auch aussehen mag (und er sieht bestenfalls wackelig aus), es besteht das Risiko, dass Sie etwas übersehen, und möchten Sie diese Chance wirklich nutzen, wenn es um Sicherheit geht?
Verwenden Sie Parameter.
quelle
Und dann geht jemand und verwendet "statt". Parameter sind, IMO, der einzig sichere Weg.
Es vermeidet auch viele i18n-Probleme mit Datums- / Zahlenangaben. Welches Datum ist der 01.02.03? Wie viel ist 123.456? Stimmen Ihre Server (App-Server und DB-Server) überein?
Wenn der Risikofaktor sie nicht überzeugt, wie steht es dann mit der Leistung? Das RDBMS kann den Abfrageplan wiederverwenden, wenn Sie Parameter verwenden, um die Leistung zu verbessern. Dies kann nicht nur mit der Zeichenfolge geschehen.
quelle
Das Argument ist ein No-Win. Wenn es Ihnen gelingt, eine Sicherheitsanfälligkeit zu finden, ändern Ihre Mitarbeiter lediglich die SafeDBString-Funktion, um dies zu berücksichtigen, und bitten Sie dann, erneut zu beweisen, dass sie unsicher ist.
Angesichts der Tatsache, dass parametrisierte Abfragen eine unbestrittene bewährte Programmiermethode sind, sollte die Beweislast bei ihnen liegen, um anzugeben, warum sie keine Methode verwenden, die sowohl sicherer als auch leistungsfähiger ist.
Wenn das Problem darin besteht, den gesamten Legacy-Code neu zu schreiben, besteht der einfache Kompromiss darin, parametrisierte Abfragen in allen neuen Codes zu verwenden und alten Code zu überarbeiten, um sie bei der Arbeit an diesem Code zu verwenden.
Ich vermute, das eigentliche Problem ist Stolz und Sturheit, und Sie können nicht viel mehr dagegen tun.
quelle
Zunächst ist Ihr Beispiel für die "Ersetzen" -Version falsch. Sie müssen Apostrophe um den Text setzen:
Das ist eine weitere Sache, die Parameter für Sie tun: Sie müssen sich keine Gedanken darüber machen, ob ein Wert in Anführungszeichen gesetzt werden muss oder nicht. Natürlich können Sie das in die Funktion einbauen, aber dann müssen Sie der Funktion eine Menge Komplexität hinzufügen: Wie erkennt man den Unterschied zwischen 'NULL' als Null und 'NULL' nur als Zeichenfolge oder zwischen einer Zahl und Eine Zeichenfolge, die zufällig viele Ziffern enthält. Es ist nur eine weitere Quelle für Fehler.
Eine andere Sache ist die Leistung: Parametrisierte Abfragepläne werden häufig besser zwischengespeichert als verkettete Pläne, wodurch der Server möglicherweise einen Schritt beim Ausführen der Abfrage spart.
Darüber hinaus ist es nicht gut genug, einfachen Anführungszeichen zu entkommen. Viele DB-Produkte ermöglichen alternative Methoden zum Entkommen von Charakteren, die ein Angreifer nutzen könnte. In MySQL können Sie beispielsweise auch ein einfaches Anführungszeichen mit einem Backslash umgehen. Und so würde der folgende "Name" -Wert MySQL nur mit der
SafeDBString()
Funktion in die Luft jagen , denn wenn Sie das einfache Anführungszeichen verdoppeln, wird das erste immer noch durch den Backslash maskiert, so dass das zweite "aktiv" bleibt:Außerdem bringt julianr einen guten Punkt unten nach oben: NIEMALS versuchen , die Sicherheit der Arbeit selbst zu tun. Es ist so einfach Sicherheit Programmierung falsch auf subtile Weise zu erhalten, erscheinen zu machen, die selbst bei gründlichen Tests zu funktionieren . Dann vergeht die Zeit und ein Jahr später haben Sie herausgefunden, dass Ihr System vor sechs Monaten geknackt wurde und Sie haben es bis dahin noch nicht einmal gewusst.
Verlassen Sie sich immer so weit wie möglich auf die für Ihre Plattform bereitgestellten Sicherheitsbibliotheken. Sie werden von Personen geschrieben, die ihren Lebensunterhalt mit Sicherheitscodes verdienen, viel besser getestet als das, was Sie verwalten können, und vom Anbieter gewartet, wenn eine Sicherheitslücke gefunden wird.
quelle
Also würde ich sagen:
1) Warum versuchen Sie, etwas, das eingebaut ist, erneut zu implementieren? Es ist da, leicht verfügbar, einfach zu bedienen und bereits weltweit getestet. Wenn zukünftige Fehler darin gefunden werden, werden sie behoben und stehen allen sehr schnell zur Verfügung, ohne dass Sie etwas tun müssen.
2) Welche Prozesse sind vorhanden, um sicherzustellen, dass Sie nie einen Anruf zu SafeDBString verpassen? Das Fehlen an nur einer Stelle könnte eine ganze Reihe von Problemen aufwerfen. Wie viel werden Sie diese Dinge in Augenschein nehmen und überlegen, wie viel Verschwendung diese Mühe ist, wenn die akzeptierte richtige Antwort so leicht zu erreichen ist.
3) Wie sicher sind Sie, dass Sie jeden Angriffsvektor abgedeckt haben, den Microsoft (der Autor der Datenbank und der Zugriffsbibliothek) in Ihrer SafeDBString-Implementierung kennt ...
4) Wie einfach ist es, die Struktur des SQL zu lesen? Das Beispiel verwendet + Verkettung, Parameter sind sehr ähnlich wie string.Format, das besser lesbar ist.
Es gibt auch zwei Möglichkeiten, um herauszufinden, was tatsächlich ausgeführt wurde: Rollen Sie Ihre eigene LogCommand-Funktion, eine einfache Funktion ohne Sicherheitsbedenken , oder sehen Sie sich sogar eine SQL-Ablaufverfolgung an, um herauszufinden, was die Datenbank wirklich vorhat.
Unsere LogCommand-Funktion lautet einfach:
Richtig oder falsch, es gibt uns die Informationen, die wir ohne Sicherheitsprobleme benötigen.
quelle
Mit parametrisierten Abfragen erhalten Sie mehr als nur Schutz vor SQL-Injection. Sie erhalten auch ein besseres Caching-Potenzial für Ausführungspläne. Wenn Sie den SQL Server-Abfrageprofiler verwenden, sehen Sie immer noch die "genaue SQL, die in der Datenbank ausgeführt wird", sodass Sie auch beim Debuggen Ihrer SQL-Anweisungen nichts wirklich verlieren.
quelle
Ich habe beide Ansätze verwendet, um SQL-Injection-Angriffe zu vermeiden, und bevorzuge definitiv parametrisierte Abfragen. Wenn ich verkettete Abfragen verwendet habe, habe ich eine Bibliotheksfunktion verwendet, um den Variablen zu entkommen (wie mysql_real_escape_string), und wäre nicht sicher, ob ich alles in einer proprietären Implementierung behandelt habe (wie es scheint, sind Sie es auch).
quelle
Ohne die Verwendung von Parametern können Sie die Benutzereingaben nicht einfach überprüfen.
Wenn Sie die Klassen SQLCommand und SQLParameter verwenden, um DB-Aufrufe auszuführen, wird die ausgeführte SQL-Abfrage weiterhin angezeigt. Sehen Sie sich die CommandText-Eigenschaft von SQLCommand an.
Ich bin immer ein kleiner Verdächtiger des Roll-Your-Own-Ansatzes zur Verhinderung der SQL-Injection, wenn parametrisierte Abfragen so einfach zu verwenden sind. Zweitens, nur weil "es immer so gemacht wurde", heißt das nicht, dass es der richtige Weg ist.
quelle
Dies ist nur dann sicher, wenn Sie garantiert sind, dass Sie eine Zeichenfolge übergeben.
Was ist, wenn Sie irgendwann keine Zeichenfolge mehr eingeben? Was ist, wenn Sie nur eine Nummer übergeben?
Würde letztendlich werden:
quelle
Ich würde gespeicherte Prozeduren oder Funktionen für alles verwenden, damit sich die Frage nicht stellt.
Wo ich SQL in Code einfügen muss, verwende ich Parameter, was das einzige ist, was Sinn macht. Erinnern Sie die Andersdenkenden daran, dass es Hacker gibt, die schlauer sind als sie, und mit einem besseren Anreiz, den Code zu brechen, der versucht, sie zu überlisten. Mit Parametern ist das einfach nicht möglich und es ist auch nicht schwierig.
quelle
Stimmen Sie den Sicherheitsfragen sehr zu.
Ein weiterer Grund für die Verwendung von Parametern ist die Effizienz.
Datenbanken kompilieren Ihre Abfrage immer und zwischenspeichern sie. Anschließend wird die zwischengespeicherte Abfrage erneut verwendet (was für nachfolgende Anforderungen offensichtlich schneller ist). Wenn Sie Parameter verwenden, verwendet die Datenbank Ihre zwischengespeicherte Abfrage auch dann wieder, wenn sie unterschiedliche Parameter verwendet, basierend auf der SQL-Zeichenfolge, bevor die Parameter gebunden werden.
Wenn Sie jedoch keine Parameter binden, ändert sich die SQL-Zeichenfolge bei jeder Anforderung (die unterschiedliche Parameter hat) und stimmt nie mit dem überein, was sich in Ihrem Cache befindet.
quelle
Aus den bereits genannten Gründen sind Parameter eine sehr gute Idee. Wir hassen es jedoch, sie zu verwenden, da das Erstellen des Parameters und das Zuweisen seines Namens zu einer Variablen zur späteren Verwendung in einer Abfrage ein dreifaches Kopfwrack der Indirektion ist.
Die folgende Klasse umschließt den Stringbuilder, den Sie normalerweise zum Erstellen von SQL-Anforderungen verwenden. Damit können Sie parametrisierte Abfragen schreiben, ohne jemals einen Parameter erstellen zu müssen , sodass Sie sich auf SQL konzentrieren können. Ihr Code wird so aussehen ...
Ich hoffe, Sie stimmen zu, dass die Lesbarkeit des Codes erheblich verbessert wurde und die Ausgabe eine ordnungsgemäß parametrisierte Abfrage ist.
Die Klasse sieht so aus ...
quelle
Aus der sehr kurzen Zeit, in der ich SQL-Injection-Probleme untersuchen musste, kann ich erkennen, dass das Festlegen eines Werts "sicher" auch bedeutet, dass Sie die Tür zu Situationen schließen, in denen Sie möglicherweise tatsächlich Apostrophe in Ihren Daten haben möchten - was ist mit dem Namen einer Person? zB O'Reilly.
Damit bleiben Parameter und gespeicherte Prozeduren übrig.
Und ja, Sie sollten immer versuchen, Code so zu implementieren, wie Sie es jetzt wissen - nicht nur, wie es immer gemacht wurde.
quelle
''
als Literal angezeigt'
, sodass Ihre Zeichenfolge intern als Zeichenfolge angezeigt wirdO'Reilly
. Das ist es, was die Datenbank speichert, abruft, vergleicht usw. Wenn Sie dem Benutzer seine Daten anzeigen möchten, nachdem Sie ihm entkommen sind, behalten Sie eine Kopie der nicht entkappten Zeichenfolge appside.Hier sind einige Artikel, die Sie möglicherweise hilfreich finden, um Ihre Mitarbeiter zu überzeugen.
http://www.sommarskog.se/dynamic_sql.html
http://unixwiz.net/techtips/sql-injection.html
Persönlich ziehe ich es vor, niemals zuzulassen, dass dynamischer Code meine Datenbank berührt, sodass alle Kontakte über sps erfolgen müssen (und nicht über dynamische SQl). Dies bedeutet, dass nichts anderes getan werden kann, als was ich den Benutzern erteilt habe, und dass interne Benutzer (mit Ausnahme der wenigen Benutzer mit Produktionszugriff für Verwaltungszwecke) nicht direkt auf meine Tabellen zugreifen und Chaos anrichten, Daten stehlen oder Betrug begehen können. Wenn Sie eine Finanzanwendung ausführen, ist dies der sicherste Weg.
quelle
Es kann kaputt gehen, die Mittel hängen jedoch von den genauen Versionen / Patches usw. ab.
Einer, der bereits angesprochen wurde, ist der Überlauf- / Kürzungsfehler, der ausgenutzt werden kann.
Ein weiteres zukünftiges Mittel wäre das Auffinden von Fehlern, die anderen Datenbanken ähnlich sind - zum Beispiel hatte der MySQL / PHP-Stack ein Fluchtproblem, weil bestimmte UTF8-Sequenzen zur Manipulation der Ersetzungsfunktion verwendet werden könnten - die Ersetzungsfunktion würde dazu verleitet , die Injektionszeichen einzuführen .
Letztendlich beruht der Ersatzsicherheitsmechanismus auf der erwarteten, aber nicht beabsichtigten Funktionalität. Da die Funktionalität nicht der beabsichtigte Zweck des Codes war, besteht eine hohe Wahrscheinlichkeit, dass eine entdeckte Eigenart Ihre erwartete Funktionalität beeinträchtigt.
Wenn Sie viel Legacy-Code haben, kann die Ersetzungsmethode als Notlösung verwendet werden, um langwieriges Umschreiben und Testen zu vermeiden. Wenn Sie neuen Code schreiben, gibt es keine Entschuldigung.
quelle
Verwenden Sie nach Möglichkeit immer parametrisierte Abfragen. Manchmal kann sogar eine einfache Eingabe ohne die Verwendung seltsamer Zeichen bereits eine SQL-Injection erstellen, wenn sie nicht als Eingabe für ein Feld in der Datenbank identifiziert wird.
Lassen Sie die Datenbank also einfach die Eingabe selbst identifizieren, ganz zu schweigen davon, dass dies auch viel Ärger erspart, wenn Sie tatsächlich seltsame Zeichen einfügen müssen, die sonst maskiert oder geändert würden. Es kann am Ende sogar wertvolle Laufzeit sparen, wenn die Eingabe nicht berechnet werden muss.
quelle
Ich habe keine anderen Antwortenden gesehen, die sich mit dieser Seite des "Warum es schlecht ist, es selbst zu tun" befasst haben, aber ich habe einen SQL-Kürzungsangriff in Betracht gezogen .
Es gibt auch die
QUOTENAME
T-SQL-Funktion, die hilfreich sein kann, wenn Sie sie nicht davon überzeugen können, Parameter zu verwenden. Es fängt eine Menge (alle?) Der entkommenen Qoute-Bedenken auf.quelle
2 Jahre später rezidivierte ich ... Jeder, der Parameter als schmerzhaft empfindet, kann meine VS-Erweiterung QueryFirst ausprobieren . Sie bearbeiten Ihre Anfrage in einer echten SQL-Datei (Validation, Intellisense). Um einen Parameter hinzuzufügen, geben Sie ihn einfach direkt in Ihre SQL ein und beginnen mit dem '@'. Wenn Sie die Datei speichern, generiert QueryFirst Wrapper-Klassen, mit denen Sie die Abfrage ausführen und auf die Ergebnisse zugreifen können. Es wird den DB-Typ Ihres Parameters nachschlagen und ihn einem .net-Typ zuordnen, den Sie als Eingabe für die generierten Execute () -Methoden finden.Einfacher geht es nicht . Es richtig zu machen ist radikal schneller und einfacher als es auf andere Weise zu tun, und das Erstellen einer SQL-Injection-Schwachstelle wird unmöglich oder zumindest pervers schwierig. Es gibt noch weitere Vorteile, z. B. das Löschen von Spalten in Ihrer Datenbank und das sofortige Anzeigen von Kompilierungsfehlern in Ihrer Anwendung.
Haftungsausschluss: Ich habe QueryFirst geschrieben
quelle
Hier einige Gründe für die Verwendung parametrisierter Abfragen:
quelle
Es gab nur wenige Sicherheitslücken (ich kann mich nicht erinnern, um welche Datenbank es sich handelte), die mit dem Pufferüberlauf der SQL-Anweisung zusammenhängen.
Was ich sagen möchte ist, dass SQL-Injection mehr ist als nur "dem Zitat entkommen", und Sie haben keine Ahnung, was als nächstes kommt.
quelle
Ein weiterer wichtiger Aspekt ist die Verfolgung von entgangenen und nicht entführten Daten. Es gibt Unmengen von Anwendungen, Web und andere, die nicht richtig zu verfolgen scheinen, wenn Daten Roh-Unicode, & -kodiertes, formatiertes HTML usw. sind. Es ist offensichtlich, dass es schwierig wird, zu verfolgen, welche Zeichenfolgen
''
codiert sind und welche nicht.Es ist auch ein Problem, wenn Sie am Ende den Typ einer Variablen ändern - vielleicht war es früher eine Ganzzahl, aber jetzt ist es eine Zeichenfolge. Jetzt hast du ein Problem.
quelle