Warum hat sich der SQL Injection Prevention-Mechanismus in Richtung der Verwendung parametrisierter Abfragen entwickelt?


59

So wie ich das sehe, können SQL-Injection-Angriffe verhindert werden durch:

  1. Sorgfältiges Prüfen, Filtern und Codieren von Eingaben (vor dem Einfügen in SQL)
  2. Verwendung von vorbereiteten Anweisungen / parametrisierten Abfragen

Ich nehme an, dass es für jeden das Für und Wider gibt, aber warum hat sich # 2 durchgesetzt und wurde mehr oder weniger als der tatsächliche Weg zur Verhinderung von Injektionsangriffen angesehen? Ist es nur sicherer und weniger fehleranfällig oder gab es andere Faktoren?

Soweit ich weiß, kann # 1 genauso effektiv sein wie # 2, wenn man es richtig anwendet und alle Vorbehalte beachtet.

Bereinigen, Filtern und Codieren

Ich war verwirrt darüber, was Bereinigen , Filtern und Codieren bedeutete. Ich werde sagen , dass für meine Zwecke, die alle der oben für Option 1. In diesem Fall in Betracht gezogen wird Ich verstehe , dass Hygienisierung und Filterung das Potenzial hat , zu ändern oder zu verwerfen Eingangsdaten, während Codierung erhalten Daten , wie sie ist , sondern kodiert sie richtig, um Injektionsangriffe zu vermeiden. Ich glaube, dass das Entkommen von Daten als eine Art der Verschlüsselung angesehen werden kann.

Parametrisierte Abfragen vs. Codierungsbibliothek

Es gibt Antworten, bei denen Konzepte von parameterized queriesund encoding librariesdie austauschbar behandelt werden. Korrigieren Sie mich, wenn ich falsch liege, aber ich habe den Eindruck, dass sie unterschiedlich sind.

Mein Verständnis ist, dass sie encoding libraries, egal wie gut sie sind, immer das Potenzial haben , SQL "Program" zu ändern, weil sie Änderungen an SQL selbst vornehmen, bevor es an das RDBMS gesendet wird.

Parameterized queries Senden Sie andererseits das SQL-Programm an das RDBMS, das dann die Abfrage optimiert, den Abfrageausführungsplan definiert, die zu verwendenden Indizes usw. auswählt und dann die Daten als letzten Schritt im RDBMS einfügt selbst.

Codierungsbibliothek

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Parametrisierte Abfrage

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Historische Bedeutung

In einigen Antworten wird erwähnt, dass in der Vergangenheit aus Leistungsgründen parametrisierte Abfragen (PQ) erstellt wurden und bevor Injection-Angriffe, die auf Codierungsprobleme abzielten, populär wurden. Irgendwann stellte sich heraus, dass PQ auch gegen Injektionsattacken ziemlich wirksam war. Um dem Geist meiner Frage gerecht zu werden, warum blieb PQ die Methode der Wahl und warum entwickelte es sich besser als die meisten anderen Methoden, wenn es darum geht, SQL-Injection-Angriffe zu verhindern?


1
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft

23
Vorbereitete Anweisungen sind nicht das Ergebnis von SQL-Injection-Angriffen. Sie waren von Anfang an dabei. Ihre Frage basiert auf einer falschen Prämisse.
user207421

4
Wenn Sie denken, Sie sind schlauer als die Bösen, dann entscheiden Sie sich für # 1
paparazzo

1
"Warum ist PQ die Methode der Wahl geblieben?" Weil es die einfachste und robusteste ist. Plus die oben genannten Leistungsvorteile gegenüber PQs. Es gibt wirklich keinen Nachteil.
Paul Draper

1
Denn es ist die richtige Lösung für das Problem, wie Abfragen ausgeführt werden sollen, auch wenn das Problem der SQL-Injection in einem Sicherheitskontext nicht gegeben ist . Formulare, bei denen es erforderlich ist, In-Band-Daten mit Befehlen zu maskieren und zu verwenden, sind immer ein Konstruktionsfehler, da sie fehleranfällig, nicht intuitiv sind und bei falscher Verwendung fehlerhaft funktionieren. Siehe auch: Shell-Scripting.
R ..

Antworten:


147

Das Problem ist, dass Sie bei Nummer 1 die gesamte SQL-Variante, gegen die Sie arbeiten, analysieren und interpretieren müssen, damit Sie wissen, ob sie etwas tut, was sie nicht tun sollte. Halten Sie diesen Code auf dem neuesten Stand, wenn Sie Ihre Datenbank aktualisieren. Überall akzeptieren Sie Eingaben für Ihre Abfragen. Und nicht vermasseln.

Ja, so etwas würde SQL-Injection-Angriffe stoppen, aber die Implementierung ist absurderweise teurer.


60
@dennis - Nun, was ist ein Zitat in Ihrer SQL-Variante? "? '?"? U + 2018? \ U2018? Gibt es Tricks, um Ausdrücke zu trennen? Können Ihre Unterabfragen aktualisiert werden? Es gibt viele Dinge zu beachten.
Telastyn

7
@Dennis Jede DB-Engine hat ihre eigene Methode, um beispielsweise Zeichen in Strings zu entkommen. Dies ist eine Menge Lücken, die es zu schließen gilt, insbesondere wenn eine Anwendung mit mehreren DB-Engines arbeiten oder mit zukünftigen Versionen derselben Engine kompatibel sein muss, die möglicherweise eine geringfügige Abfragesyntax ändern, die ausgenutzt werden könnte.

12
Ein weiterer Vorteil von vorbereiteten Anweisungen ist der Leistungsgewinn, den Sie erzielen, wenn Sie dieselbe Abfrage mit unterschiedlichen Werten erneut ausführen müssen. Außerdem können vorbereitete Anweisungen wissen, ob ein Wert wirklich als nulleine Zeichenfolge oder eine Zahl gemeint ist , und dementsprechend handeln. Dies ist sehr gut für die Sicherheit. Und selbst wenn Sie die Abfrage einmal ausführen, hat sie die DB-Engine bereits für Sie optimiert. Besser noch, wenn es zwischengespeichert ist!
Ismael Miguel

8
@Dennis Mr. Henry Null wird Ihnen dafür danken, dass Sie das richtig gemacht haben.
Mathieu Guindon

14
@ Tennis der Vorname ist irrelevant. Das Problem liegt beim Nachnamen. Siehe Stapelüberlauf , Programmierer.SE , Fox Sports , Wired , BBC und was auch immer Sie in einer schnellen Google-Suche auftauchen können ;-)
Mathieu Guindon

80

Weil Option 1 keine Lösung ist. Durchsuchen und Filtern bedeutet, ungültige Eingaben zurückzuweisen oder zu entfernen. Aber jede Eingabe kann gültig sein. Zum Beispiel ist Apostroph ein gültiges Zeichen im Namen "O'Malley". Es muss nur korrekt codiert werden, bevor es in SQL verwendet wird, wie es vorbereitete Anweisungen tun.


Nachdem Sie die Notiz hinzugefügt haben, scheinen Sie im Grunde zu fragen, warum Sie eine Standardbibliotheksfunktion verwenden sollen, anstatt Ihren eigenen funktionsähnlichen Code von Grund auf zu schreiben. Sie sollten immer Standardbibliothekslösungen dem Schreiben Ihres eigenen Codes vorziehen. Es ist weniger Arbeit und mehr wartbar. Dies gilt für alle Funktionen, aber insbesondere für sicherheitsrelevante Dinge ist es absolut sinnlos, das Rad selbst neu zu erfinden.


2
Das war's (und das war der fehlende Teil in zwei anderen Antworten, also +1). Bedenkt man , wie die Frage formuliert ist, geht es nicht um desinfizierende Benutzereingaben, aber, ich zitiere die Frage: „Filtereingang (vor dem Einsetzen)“. Wenn es jetzt um die Bereinigung der Eingabe geht, warum sollten Sie es dann selbst tun, anstatt die Bibliothek dies tun zu lassen (und dabei auch die Möglichkeit zu lassen, Ausführungspläne im Cache zu haben)?
Arseni Mourzenko

8
@Dennis: Desinfizieren oder Filtern bedeutet , Informationen zu entfernen . Codieren bedeutet, die Darstellung von Daten zu transformieren, ohne Informationen zu verlieren.
JacquesB

9
@Dennis: Filtern bedeutet, dass Benutzereingaben entweder akzeptiert oder abgelehnt werden. Beispielsweise würde "Jeff" als Eingabe des Felds "Alter des Benutzers " gefiltert , da der Wert offensichtlich ungültig ist. Wenn Sie die Eingabe nicht filtern, sondern umwandeln, indem Sie beispielsweise das einfache Anführungszeichen ersetzen, tun Sie genau dasselbe wie in den Datenbankbibliotheken, in denen parametrisierte Abfragen verwendet werden. In diesem Fall lautet Ihre Frage einfach: „Warum sollte ich etwas verwenden, das existiert und von Fachleuten geschrieben wurde, wenn ich das Rad in jedem Projekt neu erfinden kann?“
Arseni Mourzenko,

3
@Dennis: O\'Malleyverwendet den Schrägstrich, um das Anführungszeichen für das ordnungsgemäße Einfügen zu umgehen (zumindest in einigen Datenbanken). In MS SQL oder Access kann es mit einem zusätzlichen Anführungszeichen maskiert werden O''Malley. Nicht sehr tragbar, wenn Sie es selbst tun müssen.
AbraCadaver

5
Ich kann Ihnen nicht sagen, wie oft mein Name von einem System komplett abgelehnt wurde. Manchmal habe ich sogar Fehler gesehen, die durch SQL-Injection verursacht wurden, nur weil ich meinen Namen verwendet habe. Heck, ich wurde einmal gebeten, meinen Benutzernamen zu ändern, weil ich tatsächlich etwas im Backend kaputt gemacht habe.
Alexander O'Mara

60

Wenn Sie versuchen, Zeichenfolgen zu verarbeiten, wird keine SQL-Abfrage generiert. Sie generieren eine Zeichenfolge, die eine SQL-Abfrage erzeugen kann. Es gibt eine Indirektionsebene, die viel Raum für Fehler und Bugs eröffnet. Es ist wirklich etwas überraschend, da wir in den meisten Kontexten gerne programmatisch mit etwas interagieren. Wenn wir beispielsweise eine Listenstruktur haben und ein Element hinzufügen möchten, tun wir dies normalerweise nicht:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Wenn jemand vorschlägt, das zu tun, würden Sie zu Recht antworten, dass es ziemlich lächerlich ist, und dass man einfach tun sollte:

List<Integer> list = /* ... */;
list.add(5, position=2);

Das interagiert mit der Datenstruktur auf konzeptioneller Ebene. Es wird keine Abhängigkeit davon eingeführt, wie diese Struktur gedruckt oder analysiert werden könnte. Das sind völlig orthogonale Entscheidungen.

Ihr erster Ansatz ähnelt dem ersten Beispiel (nur ein bisschen schlechter): Sie gehen davon aus, dass Sie programmgesteuert die Zeichenfolge erstellen können, die korrekt als die von Ihnen gewünschte Abfrage analysiert wird. Das hängt vom Parser und einer ganzen Reihe von Zeichenfolgenverarbeitungslogiken ab.

Der zweite Ansatz für die Verwendung vorbereiteter Abfragen ähnelt dem zweiten Beispiel. Wenn Sie eine vorbereitete Abfrage verwenden, analysieren Sie im Wesentlichen eine Pseudoabfrage, die legal ist, aber einige Platzhalter enthält, und verwenden dann eine API, um einige Werte dort korrekt zu ersetzen. Der Parsing-Prozess entfällt, und Sie müssen sich keine Gedanken mehr über die Verarbeitung von Zeichenfolgen machen.

Im Allgemeinen ist es viel einfacher und weniger fehleranfällig, mit Dingen auf konzeptioneller Ebene zu interagieren. Eine Abfrage ist keine Zeichenfolge. Sie erhalten eine Abfrage, wenn Sie eine Zeichenfolge analysieren oder programmgesteuert erstellen (oder mit einer anderen Methode können Sie eine erstellen).

Hier gibt es eine gute Analogie zwischen Makros im C-Stil, die einfachen Text ersetzen, und Makros im Lisp-Stil, die beliebigen Code generieren. Mit Makros im C-Stil können Sie Text im Quellcode ersetzen. Dies bedeutet, dass Sie syntaktische Fehler oder irreführendes Verhalten einführen können. Mit Lisp-Makros generieren Sie Code in der Form, in der der Compiler ihn verarbeitet (dh Sie geben die tatsächlichen Datenstrukturen zurück, die der Compiler verarbeitet, und nicht Text, den der Leser verarbeiten muss, bevor der Compiler darauf zugreifen kann). . Mit einem Lisp-Makro können Sie jedoch keinen Parser-Fehler generieren. Sie können zB nicht generieren (let ((ab) a .

Selbst mit Lisp-Makros können Sie immer noch fehlerhaften Code generieren, da Sie sich nicht unbedingt der Struktur bewusst sind, die vorhanden sein soll. In Lisp bedeutet (let ((ab)) a) beispielsweise "eine neue lexikalische Bindung der Variablen a an den Wert der Variablen b herstellen und dann den Wert von a zurückgeben", und (let (ab) a) bedeutet "Stellen Sie neue lexikalische Bindungen der Variablen a und b her, initialisieren Sie beide auf null und geben Sie dann den Wert von a zurück." Diese sind beide syntaktisch korrekt, aber sie bedeuten verschiedene Dinge. Um dieses Problem zu vermeiden, können Sie semantischere Funktionen verwenden und folgende Aktionen ausführen:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

Mit so etwas ist es unmöglich , etwas zurückzugeben, das syntaktisch ungültig ist, und es ist viel schwieriger , etwas zurückzugeben, das versehentlich nicht Ihren Wünschen entspricht.


Gute Erklärung!
Mike Partridge

2
Sie haben mich bei "guter Analogie" verloren, aber ich habe auf der Grundlage der vorstehenden Erklärung abgestimmt. :)
Wildcard

1
Exzellentes Beispiel! - Und Sie könnten hinzufügen: Je nach Datentyp ist es manchmal nicht möglich oder machbar, eine syntaktisch analysierbare Zeichenfolge zu erstellen. - Was passiert , wenn einer meiner Parameter ist ein Freitextfeld eine Geschichte Entwurf enthält (~ 10.000 Zeichen)? oder was ist, wenn ein Parameter ein JPG-Bild ist ? - Der einzige Weg ist dann eine parametrisierte Abfrage
Falco

Eigentlich nein - es ist eine ziemlich schlechte Beschreibung, warum sich vorbereitete Aussagen als Verteidigung gegen SQL Injection entwickelten. Insbesondere das Codebeispiel ist in Java, das nicht vorhanden war, als parametrisierte Abfragen wahrscheinlich in dem Zeitraum entwickelt wurden, in dem C / C ++ als Stand der Technik angesehen wurde. Die Verwendung von SQL-Datenbanken begann in den ersten Jahren des Zeitraums 1970-1980. WEG vor übergeordneten Sprachen, wo beliebt. Heck, ich würde sagen, viele von ihnen kamen, um die Arbeit mit Datenbanken zu erleichtern (PowerBuilder jemand?)
TomTom

@TomTom Eigentlich stimme ich den meisten Ihrer Inhalte zu. Ich habe hier nur implizit den Sicherheitsaspekt angesprochen. Auf SO beantworte ich viele SPARQL-Fragen (die RDF-Abfragesprache mit einigen Ähnlichkeiten zu SQL), und viele Leute stoßen auf Probleme, weil sie Zeichenfolgen verketten, anstatt parametrisierte Abfragen zu verwenden. Auch ohne Injection-Angriffe tragen parametrisierte Abfragen dazu bei, Bugs / Crashes zu vermeiden, und Bugs / Crashes können auch Sicherheitsprobleme darstellen, selbst wenn es sich nicht um Injection-Angriffe handelt. Also würde ich immer weniger sagen: Parametrisierte Abfragen sind gut, auch wenn SQL-Injection kein Problem darstellt, und sie sind gut ...
Joshua Taylor

21

Es ist hilfreich, dass Option 2 im Allgemeinen als bewährte Methode angesehen wird, da die Datenbank die nicht parametrisierte Version der Abfrage zwischenspeichern kann. Parametrisierte Abfragen liegen mehrere Jahre vor dem Problem der SQL-Injection (glaube ich). Es kommt also vor, dass Sie zwei Fliegen mit einer Klappe schlagen können.


10
SQL-Injection ist seit der Erfindung von SQL ein Problem. Es wurde später kein Problem mehr.
Servy

9
@Servy Theoretisch ja. Praktisch wurde es erst zu einem echten Problem, als unsere Eingabemechanismen online gingen und eine massive Angriffsfläche für jedermann zum Hämmern darstellten.
Jan Doggen

8
Little Bobby Tables würde nicht zustimmen, dass Sie weder das Internet noch eine große Benutzerbasis benötigen, um SQL-Injection zu nutzen. Und natürlich datieren Netzwerke SQL vor. Es ist also nicht so, als müssten Sie nach dem Erscheinen von SQL auf Netzwerke warten. Ja, Sicherheitslücken sind weniger anfällig , wenn die Anwendung eine kleine Nutzerbasis hat, aber sie sind immer noch Sicherheitslücken, und die Menschen haben sie ausnutzen , wenn die Datenbank selbst wertvolle Daten hat (und viele sehr früh Datenbank hatte sehr wertvolle Daten, wie nur Menschen mit wertvollen Datenbanken könnte sich die Tech leisten) ..
Servy

5
@ Meines Wissens nach war dynamisches SQL ein relativ spätes Feature. Die anfängliche Verwendung von SQL wurde größtenteils mit Parametern für Werte (sowohl in als auch out) vorkompiliert / vorverarbeitet, sodass Parameter in Abfragen möglicherweise vor der SQL-Injection in Software liegen (möglicherweise nicht in Ad-hoc- / CLI-Abfragen).
Mark Rotteveel

6
Sie könnten älter sein als das Bewusstsein für SQL-Injection.
user253751

20

Einfach gesagt: Das haben sie nicht. Deine Meinung:

Warum hat sich der SQL Injection Prevention-Mechanismus in Richtung der Verwendung parametrisierter Abfragen entwickelt?

ist grundsätzlich fehlerhaft. Parametrisierte Abfragen existieren schon viel länger als SQL Injection zumindest allgemein bekannt ist. Sie wurden im Allgemeinen entwickelt, um die Konzentration von Zeichenfolgen in der üblichen "Form for Search" -Funktionalität zu vermeiden, die LOB-Anwendungen (Line of Business) haben. Viele - VIELE - Jahre später stellte jemand eine Sicherheitslücke bei der Manipulation der Zeichenfolgen fest.

Ich erinnere mich an SQL vor 25 Jahren (als das Internet noch nicht so verbreitet war - es fing gerade erst an) und an SQL im Vergleich zu IBM DB5 IIRC Version 5 - und das hatte bereits parametrisierte Abfragen.


Vielen Dank. Warum musste die Verkettung von Zeichenfolgen vermieden werden? Mir scheint, das wäre eine nützliche Funktion. Hatte jemand ein Problem damit?
Dennis

3
Eigentlich zwei. Erstens ist es nicht immer ganz einfach - warum sich mit Speicherzuweisung usw. befassen, wenn sie nicht benötigt wird? Aber zweitens war die Performance-Caching-SQL-Datenbankseite in früheren Zeiten nicht gerade so großartig - SQL-Kompilierung war teuer. Als Nebeneffekt der Verwendung von SQL-vorbereiteten Anweisungen (von denen die Parameter stammen) können Ausweichpläne wiederverwendet werden. SQL Server führte die automatische Parametrisierung ein (um Abfragepläne auch ohne Parameter wiederzuverwenden - sie werden abgezogen und impliziert). Ich denke, entweder 2000 oder 2007 - irgendwo dazwischen, IIRC.
TomTom

2
Durch parametrisierte Abfragen wird die Möglichkeit der Zeichenfolgenverkettung nicht eingeschränkt. Sie können Zeichenfolgen verketten, um eine parametrisierte Abfrage zu generieren. Nur weil eine Funktion nützlich ist, bedeutet dies nicht, dass sie für ein bestimmtes Problem immer eine gute Wahl ist.
JimmyJames

Ja, aber wie gesagt - zu der Zeit, als sie erfunden wurden, hatte dynamisches SQL einen recht ordentlichen Leistungseinbruch. Noch heute wird Ihnen mitgeteilt, dass dynamische SQL-Abfragepläne in SQL Server nicht wiederverwendet werden (was falsch ist, da - hm - as Ich sagte irgendwann zwischen 2000 und 2007 - also ziemlich lange). Zu dieser alten Zeit wollten Sie wirklich PREPARED-Anweisungen, wenn Sie SQL mehrfach ausführen;)
TomTom

Plan - Caching für dynamische SQL war in der Tat hinzugefügt SQL Server 7.0 in 1998 - sqlmag.com/database-performance-tuning/...
Mike Dimmick

13

Neben all den anderen guten Antworten:

Der Grund, warum # 2 besser ist, ist, dass es Ihre Daten von Ihrem Code trennt. In der Nummer 1 sind Ihre Daten Teil Ihres Codes und daher kommen all die schlechten Dinge. Mit # 1 erhalten Sie Ihre Abfrage und müssen zusätzliche Schritte ausführen, um sicherzustellen, dass Ihre Abfrage Ihre Daten als Daten versteht, während Sie mit # 2 Ihren Code und seinen Code erhalten und Ihre Daten Daten sind.


3
Die Trennung von Code und Daten bedeutet auch, dass Ihre Abwehrmaßnahmen gegen feindliche Codeinjektionen vom Datenbankanbieter geschrieben und getestet werden. Wenn also etwas, das als Parameter zusammen mit einer harmlosen Abfrage übergeben wurde, Ihre Datenbank in den Papierkorb wirft oder untergräbt, ist die Reputation des Datenbankunternehmens in Gefahr, und Ihre Organisation könnte sie sogar verklagen und gewinnen. Es bedeutet auch, dass, wenn dieser Code einen ausnutzbaren Fehler enthält, die Chancen ziemlich gut stehen, dass es sich um die Site eines anderen handelt, auf der sich alles Mögliche abspielt, und nicht um Ihre. (Ignorieren Sie einfach nicht die Sicherheits-Bugfixes!)
Nigel222

11

Parametrisierte Abfragen bieten neben der SQL-Injection-Abwehr häufig den zusätzlichen Vorteil, dass sie nur einmal kompiliert und dann mehrmals mit unterschiedlichen Parametern ausgeführt werden.

Aus der SQL - Datenbank Sicht select * from employees where last_name = 'Smith'und select * from employees where last_name = 'Fisher'sind deutlich unterschiedlich und erfordern daher getrennte Analyse, Erstellung und Optimierung. Sie belegen auch separate Slots im Speicherbereich, in denen kompilierte Anweisungen gespeichert werden. In einem stark ausgelasteten System mit einer großen Anzahl ähnlicher Abfragen, die unterschiedliche Parameter aufweisen, können die Berechnung und der Speicheraufwand erheblich sein.

Anschließend bietet die Verwendung parametrisierter Abfragen häufig erhebliche Leistungsvorteile.


Ich denke, das ist die Theorie (basierend auf verwendeten vorbereiteten Anweisungen für parametrisierte Abfragen). In der Praxis bezweifle ich, dass dies tatsächlich so häufig der Fall ist, da die meisten Implementierungen nur in einem Aufruf vorbereiten, binden und ausführen. Verwenden Sie daher für jede parametrisierte Abfrage eine andere vorbereitete Anweisung, es sei denn, Sie führen explizite Schritte aus, um Anweisungen (und eine Bibliothek) tatsächlich vorzubereiten -level unterscheidet preparesich oft stark von einem tatsächlichen SQL-Level prepare.
Jcaron

Die folgenden Abfragen unterscheiden sich auch vom SQL-Parser: SELECT * FROM employees WHERE last_name IN (?, ?)und SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick

Ja, sie haben. Aus diesem Grund hat MS SQL Server 7 bereits 1998 einen Abfrageplan hinzugefügt. Wie in: Ihre Informationen sind eine Generation alt.
TomTom

1
@TomTom - Das Zwischenspeichern von Abfrageplänen ist nicht dasselbe wie die automatische Parametrisierung, auf die Sie anscheinend hinweisen. Wie in, lies bevor du postest.
Mustaccio

@mustaccio Tatsächlich hat mindestens MS beide gleichzeitig eingeführt.
TomTom

5

Warte aber warum?

Option 1 bedeutet, dass Sie für jede Art von Eingabe Desinfektionsroutinen schreiben müssen, während Option 2 weniger fehleranfällig ist und Sie weniger Code schreiben / testen / warten müssen.

Mit ziemlicher Sicherheit kann das "Aufpassen aller Vorbehalte" komplexer sein, als Sie denken, und Ihre Sprache (z. B. Java PreparedStatement) enthält mehr Informationen als Sie denken.

Vorbereitete Anweisungen oder parametrisierte Abfragen werden auf dem Datenbankserver vorkompiliert. Wenn also Parameter festgelegt werden, erfolgt keine SQL-Verkettung, da die Abfrage keine SQL-Zeichenfolge mehr ist. Ein zusätzlicher Vorteil ist, dass das RDBMS die Abfrage zwischenspeichert und nachfolgende Aufrufe als dieselbe SQL betrachtet werden, auch wenn die Parameterwerte variieren, während bei verkettetem SQL jedes Mal, wenn die Abfrage mit unterschiedlichen Werten ausgeführt wird, die Abfrage unterschiedlich ist und das RDBMS sie analysieren muss , erstellen Sie den Ausführungsplan erneut usw.


1
JDBC bereinigt nicht das Anithing. Protokoll hat einen spezifischen Teil für Parameter und DB interpretiert diese Parameter einfach nicht. Aus diesem Grund können Sie den Tabellennamen über Parameter festlegen.
Talex

1
Warum? Wenn der Parameter nicht analysiert oder interpretiert wird, gibt es keinen Grund, etwas zu umgehen.
Talex

11
Ich denke, Sie haben das falsche Bild davon, wie eine parametrisierte Abfrage funktioniert. Es ist nicht nur so, dass die Parameter später ersetzt werden, sie werden auch nie ersetzt . Ein DBMS verwandelt jede Abfrage in einen "Plan", eine Reihe von Schritten, die ausgeführt werden, um Ihr Ergebnis zu erhalten. In einer parametrisierten Abfrage ähnelt dieser Plan einer Funktion: Er enthält eine Reihe von Variablen, die bei der Ausführung angegeben werden müssen. Zum Zeitpunkt der Bereitstellung der Variablen wurde die SQL-Zeichenfolge vollständig vergessen, und der Plan wird nur mit den angegebenen Werten ausgeführt.
IMSoP

2
@IMSoP Das war ein Missverständnis von mir. Obwohl ich denke, es ist eine gemeinsame, wie Sie in den beiden am häufigsten gestellten Antworten auf diese Frage in SO stackoverflow.com/questions/3271249/… sehen können . Ich habe darüber gelesen und du hast recht. Ich habe die Antwort bearbeitet.
Tulains Córdova

3
@TomTom Das ist großartig für die Leistung , tut aber nichts für die Sicherheit . Bis ein gefährdetes Stück dynamisches SQL kompiliert und zwischengespeichert wird, wurde das Programm bereits geändert . Das Erstellen eines Plans aus nicht dynamisch parametrisiertem SQL und das anschließende Übergeben von Datenelementen unterscheidet sich noch grundlegend von einem DBMS, das die Ähnlichkeit zwischen zwei Abfragen abstrahiert, die als vollständige SQL-Zeichenfolgen dargestellt werden.
IMSoP

1

Stellen wir uns vor, wie ein idealer "Desinfizieren, Filtern und Codieren" -Ansatz aussehen würde.

Bereinigung und Filterung sind möglicherweise im Kontext einer bestimmten Anwendung sinnvoll, aber letztendlich bedeuten beide, dass Sie diese Daten nicht in die Datenbank stellen können. Für Ihre Anwendung mag das eine gute Idee sein, aber es ist nichts, was Sie als allgemeine Lösung empfehlen können, da es Anwendungen geben wird, die beliebige Zeichen in der Datenbank speichern können müssen.

So bleibt die Codierung. Sie könnten mit einer Funktion beginnen, die Zeichenfolgen durch Hinzufügen von Escape-Zeichen codiert, damit Sie diese in sich selbst ersetzen können. Da unterschiedliche Datenbanken unterschiedliche Escape- Zeichen benötigen (in einigen Datenbanken beide \'und ''gültige Escape-Sequenzen für ', in anderen jedoch nicht), muss diese Funktion vom Datenbankanbieter bereitgestellt werden.

Aber nicht alle Variablen sind Zeichenfolgen. Manchmal müssen Sie eine ganze Zahl oder ein Datum eingeben. Diese Zeichenfolgen werden anders dargestellt als Zeichenfolgen. Sie benötigen daher unterschiedliche Codierungsmethoden (diese müssten wiederum für den Datenbankanbieter spezifisch sein), und Sie müssen sie auf unterschiedliche Weise in die Abfrage einsetzen.

Vielleicht wäre es einfacher, wenn die Datenbank auch die Ersetzung für Sie übernehmen würde - sie weiß bereits, welche Typen die Abfrage erwartet, wie Daten sicher verschlüsselt und wie sie sicher in Ihre Abfrage eingefügt werden, sodass Sie sich keine Sorgen machen müssen es in Ihrem Code.

Zu diesem Zeitpunkt haben wir parametrisierte Abfragen neu erfunden.

Einmal parametrisierte Abfragen eröffnen neue Möglichkeiten wie Leistungsoptimierungen und eine vereinfachte Überwachung.

Das Codieren ist schwierig und das Codieren ist nicht von der Parametrierung zu unterscheiden.

Wenn Sie die Zeichenfolgeninterpolation als Methode zum Erstellen von Abfragen wirklich mögen, fallen Ihnen einige Sprachen (Scala und ES2015) ein, die über eine steckbare Zeichenfolgeninterpolation verfügen. Daher gibt es Bibliotheken , mit denen Sie parametrisierte Abfragen schreiben können, die wie Zeichenfolgeninterpolation aussehen sind vor SQL-Injection sicher - so in der ES2015-Syntax:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
"Kodierung ist schwer richtig zu machen" - hahaha. Es ist nicht. Ein oder zwei Tage ist alles dokumentiert. Ich habe vor vielen Jahren einen Encoder für ein ORM geschrieben (da SQL Server eine Beschränkung für Parameter hat und es daher problematisch ist, 5000-10000 Zeilen in eine Anweisung einzufügen (vor 15 Jahren). Ich erinnere mich nicht, dass dies ein großes Problem war.
TomTom

1
Möglicherweise ist SQL Server so regelmäßig, dass es kein Problem darstellt, aber ich habe Probleme mit anderen DBs festgestellt - Eckfälle mit nicht übereinstimmenden Zeichencodierungen, verdeckten Konfigurationsoptionen, bestimmten Datums- und Nummernproblemen in der Ländereinstellung. Alles lösbar, aber zumindest ein flüchtiges Verständnis der Macken der DB erforderlich (ich sehe Sie, MySQL und Oracle).
James_pic

3
@TomTom Encoding ist eigentlich sehr schwer zu finden, wenn man die Zeit berücksichtigt. Was tun Sie, wenn Ihr DB-Anbieter beschließt, in der nächsten Version einen neuen Kommentarstil zu erstellen, oder wenn ein Bareword in einem Upgrade zu einem neuen Schlüsselwort wird? Theoretisch könnte die Codierung für eine Version Ihres RDBMS richtig sein und Sie könnten sich bei der nächsten Revision irren. Erfahren Sie nicht einmal, was passiert, wenn Sie zu einem Anbieter wechseln, der bedingte Kommentare mit nicht standardmäßiger Syntax enthält
Eric,

@Eric, das ist ehrlich gesagt schrecklich. (Ich benutze Postgres; wenn es solche bizarren Warzen gibt, habe ich sie noch nicht gesehen.)
Wildcard

0

In Option 1 arbeiten Sie mit einer Eingabemenge von size = infinity, die Sie einer sehr großen Ausgabegröße zuordnen möchten. In Option 2 haben Sie Ihre Eingabe auf das beschränkt, was Sie auswählen. Mit anderen Worten:

  1. Sorgfältiges Screening und Filtern [ unendlich ] nach [ allen sicheren SQL-Abfragen ]
  2. Verwenden von [ Vorüberlegte Szenarien, die auf Ihren Bereich beschränkt sind ]

Anderen Antworten zufolge scheint es auch einige Leistungsvorteile zu geben, wenn Sie Ihren Bereich von unendlich auf etwas Verwaltbares beschränken.


0

Ein nützliches mentales Modell für SQL (insbesondere moderne Dialekte) ist, dass jede SQL-Anweisung oder -Abfrage ein Programm ist. In einem nativen ausführbaren Binärprogramm sind die gefährlichsten Sicherheitslücken Überläufe, bei denen ein Angreifer den Programmcode mit verschiedenen Anweisungen überschreiben oder ändern kann.

Eine SQL-Injection-Sicherheitsanfälligkeit ist isomorph zu einem Pufferüberlauf in einer Sprache wie C. Die Vergangenheit hat gezeigt, dass Pufferüberläufe äußerst schwer zu verhindern sind - selbst extrem kritischer Code, der einer offenen Überprüfung unterzogen wurde, enthielt häufig solche Sicherheitsanfälligkeiten.

Ein wichtiger Aspekt des modernen Ansatzes zur Behebung von Sicherheitslücken durch Überlauf ist die Verwendung von Hardware- und Betriebssystemmechanismen, um bestimmte Teile des Speichers als nicht ausführbar und andere Teile des Speichers als schreibgeschützt zu kennzeichnen. (Siehe beispielsweise den Wikipedia-Artikel zum Schutz des ausführbaren Speicherplatzes .) Auf diese Weise kann der Angreifer selbst dann, wenn ein Angreifer Daten ändern könnte, nicht bewirken, dass die eingespeisten Daten als Code behandelt werden.

Wenn eine SQL-Injection-Sicherheitsanfälligkeit einem Pufferüberlauf entspricht, wie lautet dann die SQL-Entsprechung für ein NX-Bit oder für schreibgeschützte Speicherseiten? Die Antwort lautet: vorbereitete Anweisungen , die parametrisierte Abfragen und ähnliche Mechanismen für Nicht-Abfrageanfragen enthalten. Die vorbereitete Anweisung wird mit bestimmten Teilen kompiliert, die als schreibgeschützt gekennzeichnet sind, sodass ein Angreifer diese Teile des Programms und andere Teile, die als nicht ausführbare Daten gekennzeichnet sind (die Parameter der vorbereiteten Anweisung), nicht ändern kann, in die der Angreifer jedoch Daten einfügen könnte Dies wird niemals als Programmcode behandelt, wodurch das meiste Missbrauchspotenzial beseitigt wird.

Sicherlich ist es gut, Benutzereingaben zu bereinigen, aber um wirklich sicher zu sein, müssen Sie paranoid sein (oder entsprechend wie ein Angreifer denken). Eine Steueroberfläche außerhalb des Programmtexts ist der Weg, dies zu tun, und vorbereitete Anweisungen stellen diese Steueroberfläche für SQL bereit. Kein Wunder also, dass vorbereitete Anweisungen und damit parametrisierte Abfragen der Ansatz sind, den die überwiegende Mehrheit der Sicherheitsexperten empfiehlt.


Das ist alles schön und gut, aber es geht überhaupt nicht auf die Frage nach dem Titel ein.
TomTom

1
@TomTom: Was meinst du? Die Frage ist genau, warum parametrisierte Abfragen der bevorzugte Mechanismus sind, um SQL-Injection zu verhindern. Meine Antwort erklärt, warum parametrisierte Abfragen sicherer und robuster sind, als Benutzereingaben zu bereinigen.
Daniel Pryden

Es tut mir leid, aber MEINE Frage lautet "Warum hat sich der Mechanismus zur Verhinderung der SQL-Injektion in Richtung der Verwendung parametrisierter Abfragen entwickelt?". Sie haben nicht. Es geht nicht um das Jetzt, es geht um die Geschichte.
TomTom

0

Ich schreibe bereits hier darüber: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Aber um es einfach zu halten:

Die Art und Weise, wie parametrisierte Abfragen funktionieren, besteht darin, dass die sqlQuery als Abfrage gesendet wird und die Datenbank genau weiß, was diese Abfrage bewirkt. Erst dann werden der Benutzername und die Kennwörter lediglich als Werte eingefügt. Dies bedeutet, dass sie die Abfrage nicht ausführen können, da die Datenbank bereits weiß, wie die Abfrage ausgeführt wird. In diesem Fall würde es nach einem Benutzernamen von "Niemand ODER 1 = 1 '-" und einem leeren Passwort suchen, das falsch sein sollte.

Dies ist jedoch keine vollständige Lösung, und die Eingabevalidierung muss noch durchgeführt werden, da dies keine Auswirkungen auf andere Probleme wie XSS-Angriffe hat, da Sie immer noch JavaScript in die Datenbank einfügen können. Wenn dies dann auf einer Seite ausgelesen wird, wird es abhängig von einer Ausgabeüberprüfung als normales Javascript angezeigt. Das Beste ist also, die Eingabevalidierung zu verwenden, aber parametrisierte Abfragen oder gespeicherte Prozeduren zu verwenden, um SQL-Angriffe zu stoppen


0

Ich habe noch nie SQL verwendet. Aber offensichtlich hört man, welche Probleme die Leute haben und SQL-Entwickler hatten Probleme mit dieser "SQL-Injection" -Sache. Lange konnte ich es nicht herausfinden. Und dann wurde mir klar, dass Leute SQL-Anweisungen erstellen, echte SQL-Quellanweisungen, indem sie Zeichenfolgen verketten, von denen einige von einem Benutzer eingegeben wurden. Und mein erster Gedanke an diese Erkenntnis war Schock. Totaler Schock. Ich dachte: Wie kann man so lächerlich dumm sein und in einer solchen Programmiersprache Aussagen machen? Für einen C- oder C ++ - oder Java- oder Swift-Entwickler ist dies völliger Wahnsinn.

Das heißt, es ist nicht sehr schwierig, eine C-Funktion zu schreiben, die eine C-Zeichenfolge als Argument verwendet und eine andere Zeichenfolge erzeugt, die genau wie ein Zeichenfolgenliteral im C-Quellcode aussieht, das dieselbe Zeichenfolge darstellt. Beispielsweise würde diese Funktion abc in "abc" und "abc" in "abc" und "abc" in "\" \\ "abc \\" \ "" übersetzen. (Nun, wenn das für Sie falsch aussieht, ist das HTML. Es war richtig, als ich es eingetippt habe, aber nicht, als es angezeigt wurde.) Und wenn diese C-Funktion erst einmal geschrieben ist, ist es überhaupt nicht schwierig, C-Quellcode zu generieren Der vom Benutzer bereitgestellte Text aus einem Eingabefeld wird in ein C-String-Literal umgewandelt. Das ist nicht schwer sicher zu machen. Warum SQL-Entwickler diesen Ansatz nicht verwenden würden, um SQL-Injektionen zu vermeiden, ist mir ein Rätsel.

"Desinfizieren" ist ein völlig fehlerhafter Ansatz. Der schwerwiegende Fehler besteht darin, dass bestimmte Benutzereingaben illegal sind. Am Ende haben Sie eine Datenbank, in der ein generisches Textfeld keinen Text wie den folgenden enthalten kann. Drop Table oder was auch immer Sie in einer SQL-Injection verwenden würden, um Schaden zu verursachen. Ich finde das ziemlich inakzeptabel. Wenn eine Datenbank Text speichert, sollte sie in der Lage sein, beliebigen Text zu speichern . Und der praktische Fehler ist, dass Desinfektionsmittel es nicht richtig zu machen scheinen :-(

Parametrisierte Abfragen sind natürlich das, was jeder Programmierer erwarten würde, der eine kompilierte Sprache verwendet. Das macht das Leben so viel einfacher: Sie haben eine Zeichenfolge eingegeben, und Sie müssen sie nicht einmal in eine SQL-Zeichenfolge übersetzen, sondern übergeben sie einfach als Parameter, ohne dass die Wahrscheinlichkeit besteht, dass Zeichen in dieser Zeichenfolge Schaden anrichten.

Aus der Sicht eines Entwicklers, der kompilierte Sprachen verwendet, ist das Desinfizieren etwas, das mir niemals einfällt. Das Bedürfnis nach Desinfektion ist verrückt. Parametrisierte Abfragen sind die offensichtliche Lösung des Problems.

(Ich fand Josips Antwort interessant. Er sagt im Grunde, dass Sie mit parametrisierten Abfragen jeden Angriff auf SQL stoppen können, aber dann können Sie Text in Ihrer Datenbank haben, der zum Erstellen einer JavaScript-Injektion verwendet wird :-( Nun, wir haben wieder das gleiche Problem , und ich weiß nicht, ob Javascript eine Lösung dafür hat.


-2

Das Hauptproblem besteht darin, dass Hacker Möglichkeiten gefunden haben, die Hygiene zu umgehen, während die parametrisierten Abfragen ein vorhandenes Verfahren waren, das perfekt mit den zusätzlichen Vorteilen von Leistung und Speicher zusammenarbeitet.

Einige Leute vereinfachen das Problem, da "es sich nur um einfache und doppelte Anführungszeichen handelt". Hacker haben jedoch intelligente Methoden gefunden, um eine Erkennung zu vermeiden, wie z. B. die Verwendung unterschiedlicher Codierungen oder die Verwendung von Datenbankfunktionen.

Wie auch immer, Sie mussten nur eine einzige Zeichenfolge vergessen, um eine katastrophale Datenverletzung zu verursachen. Hacker konnten Skripte automatisieren, um die gesamte Datenbank mit einer Reihe von Abfragen herunterzuladen. Wenn die Software als Open-Source-Suite oder als berühmte Business-Suite bekannt ist, können Sie einfach die Benutzer- und Kennworttabelle aufrufen.

Auf der anderen Seite war es nur eine Frage des Lernens, verkettete Abfragen zu verwenden und sich daran zu gewöhnen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.