Wenn ja, warum gibt es immer noch so viele erfolgreiche SQL-Injektionen? Nur weil einige Entwickler zu dumm sind, um parametrisierte Anweisungen zu verwenden?
Wenn ja, warum gibt es immer noch so viele erfolgreiche SQL-Injektionen? Nur weil einige Entwickler zu dumm sind, um parametrisierte Anweisungen zu verwenden?
Antworten:
Die Links, die ich in meinen Kommentaren zur Frage gepostet habe, erklären das Problem sehr gut. Ich habe meine Gefühle darüber, warum das Problem weiterhin besteht, unten zusammengefasst:
Diejenigen, die gerade erst anfangen, haben möglicherweise keine Kenntnis von SQL-Injection.
Einige kennen die SQL-Injection, denken jedoch, dass Escape die (einzige?) Lösung ist. Wenn Sie eine schnelle Google-Suche durchführen php mysql query
, wird als erste Seite die Seite angezeigt mysql_query
, auf der ein Beispiel für die Interpolation von Escape-Benutzereingaben in eine Abfrage angezeigt wird . Es gibt keine Erwähnung (zumindest nicht, dass ich sehen kann), stattdessen vorbereitete Anweisungen zu verwenden. Wie andere gesagt haben, gibt es so viele Tutorials, die Parameterinterpolation verwenden, dass es nicht wirklich überraschend ist, wie oft sie noch verwendet werden.
Unverständnis darüber, wie parametrisierte Anweisungen funktionieren. Einige denken, dass es nur ein ausgefallenes Mittel ist, um Werten zu entkommen.
Andere kennen parametrisierte Anweisungen, verwenden sie jedoch nicht, weil sie gehört haben, dass sie zu langsam sind. Ich vermute, dass viele Leute gehört haben, wie unglaublich langsam parametrisierte Aussagen sind, aber tatsächlich keine eigenen Tests durchgeführt haben. Wie Bill Karwin in seinem Vortrag betonte, sollte der Leistungsunterschied selten als Faktor herangezogen werden, wenn die Verwendung vorbereiteter Aussagen in Betracht gezogen wird. Die Vorteile der einmaligen Vorbereitung und Ausführung vieler werden häufig vergessen, ebenso wie die Verbesserungen der Sicherheit und der Wartbarkeit des Codes.
Einige verwenden überall parametrisierte Anweisungen, jedoch mit Interpolation von nicht aktivierten Werten wie Tabellen- und Spaltennamen, Schlüsselwörtern und bedingten Operatoren. Beispiele hierfür sind dynamische Suchvorgänge, bei denen Benutzer eine Reihe verschiedener Suchfelder, Vergleichsbedingungen und Sortierreihenfolgen angeben können.
Falsches Sicherheitsgefühl bei Verwendung eines ORM. ORMs ermöglichen weiterhin die Interpolation von SQL-Anweisungsteilen - siehe 5.
Programmierung ist ein großes und komplexes Thema, Datenbankverwaltung ist ein großes und komplexes Thema, Sicherheit ist ein großes und komplexes Thema. Die Entwicklung einer sicheren Datenbankanwendung ist nicht einfach - selbst erfahrene Entwickler können sich überraschen lassen.
Viele der Antworten zum Stackoverflow helfen nicht. Wenn Benutzer Fragen schreiben, die dynamisches SQL und Parameterinterpolation verwenden, fehlen häufig Antworten, die darauf hindeuten, stattdessen parametrisierte Anweisungen zu verwenden. Bei einigen Gelegenheiten wurde mein Vorschlag, vorbereitete Aussagen zu verwenden, von Leuten widerlegt - normalerweise aufgrund des wahrgenommenen inakzeptablen Leistungsaufwands. Ich bezweifle ernsthaft, dass diejenigen, die die meisten dieser Fragen stellen, in der Lage sind, die zusätzlichen Millisekunden, die für die Erstellung einer parametrisierten Aussage benötigt werden, katastrophale Auswirkungen auf ihre Anwendung zu haben.
Wenn Artikel über parametrisierte Abfragen sprechen, die SQL-Angriffe stoppen, erklären sie nicht wirklich, warum. Oft handelt es sich um "Es tut, also fragen Sie nicht warum" - möglicherweise, weil sie sich selbst nicht kennen. Ein sicheres Zeichen für einen schlechten Erzieher ist eines, das nicht zugeben kann, dass er etwas nicht weiß. Aber ich schweife ab. Wenn ich sage, ich fand es völlig verständlich, verwirrt zu sein, ist das einfach. Stellen Sie sich eine dynamische SQL-Abfrage vor
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
Eine einfache SQL-Injection wäre also, den Benutzernamen als 'OR 1 = 1--' einzugeben. Dies würde die SQL-Abfrage effektiv durchführen:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
Dies bedeutet, dass Sie alle Kunden auswählen, deren Benutzername leer ('') oder 1 = 1 ist. Dies ist ein Boolescher Wert, der true entspricht. Dann wird - verwendet, um den Rest der Abfrage zu kommentieren. Dies druckt also einfach die gesamte Kundentabelle aus oder macht damit, was Sie wollen. Wenn Sie sich anmelden, wird es mit den Berechtigungen des ersten Benutzers angemeldet, der häufig der Administrator sein kann.
Jetzt machen es parametrisierte Abfragen anders, mit Code wie:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username)
parameters.add("Pass", password)
Dabei sind Benutzername und Passwort Variablen, die auf den zugehörigen eingegebenen Benutzernamen und das Passwort verweisen
An diesem Punkt denken Sie vielleicht, dass dies überhaupt nichts ändert. Sicherlich könnten Sie immer noch so etwas wie Nobody OR 1 = 1 'in das Feld für den Benutzernamen eingeben - und so die Abfrage effektiv durchführen:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'
Und dies scheint ein gültiges Argument zu sein. Aber du würdest dich irren.
Parametrisierte Abfragen funktionieren so, 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 beeinflussen können, da die Datenbank bereits weiß, was die Abfrage tun wird. In diesem Fall würde also nach einem Benutzernamen von "Nobody OR 1 = 1 '-" und einem leeren Passwort gesucht, 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 weiterhin Javascript in die Datenbank einfügen könnten. Wenn dies dann auf einer Seite ausgelesen wird, wird es abhängig von einer Ausgabevalidierung als normales Javascript angezeigt. Das Beste ist also, weiterhin die Eingabevalidierung zu verwenden, aber parametrisierte Abfragen oder gespeicherte Prozeduren zu verwenden, um SQL-Angriffe zu stoppen.
Na gute Frage. Die Antwort ist eher stochastisch als deterministisch und ich werde versuchen, meine Ansicht anhand eines kleinen Beispiels zu erklären.
Es gibt viele Referenzen im Internet, die uns empfehlen, Parameter in unseren Abfragen zu verwenden oder gespeicherte Prozeduren mit Parametern zu verwenden, um SQL Injection (SQLi) zu vermeiden. Ich werde Ihnen zeigen, dass gespeicherte Prozeduren (zum Beispiel) nicht der Zauberstab gegen SQLi sind. Die Verantwortung liegt weiterhin beim Programmierer.
Betrachten Sie die folgende gespeicherte SQL Server-Prozedur, mit der die Benutzerzeile aus einer Tabelle 'Benutzer' abgerufen wird:
create procedure getUser
@name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
'from Users '+
'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)
Sie können die Ergebnisse erhalten, indem Sie den Benutzernamen und das Passwort als Parameter übergeben. Angenommen, das Passwort ist im freien Text (nur zur Vereinfachung dieses Beispiels), wäre ein normaler Anruf:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = 'admin'
,@pass = '!@Th1siSTheP@ssw0rd!!'
GO
Aber hier haben wir eine schlechte Programmiertechnik, die vom Programmierer innerhalb der gespeicherten Prozedur verwendet wird, so dass ein Angreifer Folgendes ausführen kann:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = 'admin'
,@pass = 'any'' OR 1=1 --'
GO
Die obigen Parameter werden als Argumente an die gespeicherte Prozedur übergeben, und der SQL-Befehl, der schließlich ausgeführt wird, lautet:
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'
..die alle Zeilen von Benutzern zurückbekommt
Das Problem hierbei ist, dass selbst wir dem Prinzip "Erstellen einer gespeicherten Prozedur und Übergeben der Felder zur Suche als Parameter" folgen, das SQLi weiterhin ausgeführt wird. Dies liegt daran, dass wir nur unsere schlechte Programmierpraxis in die gespeicherte Prozedur kopieren. Die Lösung des Problems besteht darin, unsere gespeicherte Prozedur wie folgt umzuschreiben:
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
Ich versuche zu sagen, dass die Entwickler zuerst lernen müssen, was ein SQLi-Angriff ist und wie er ausgeführt werden kann, und dann ihren Code entsprechend schützen müssen. Das blinde Befolgen von "Best Practices" ist nicht immer der sicherere Weg ... und vielleicht haben wir deshalb so viele "Best Practices" -Fehler!
Ja, die Verwendung vorbereiteter Anweisungen stoppt zumindest theoretisch alle SQL-Injektionen. In der Praxis sind parametrisierte Anweisungen möglicherweise keine wirklich vorbereiteten Anweisungen, z. B. PDO
in PHP werden sie standardmäßig emuliert, sodass sie für einen Edge-Case-Angriff offen sind .
Wenn Sie wirklich vorbereitete Aussagen verwenden, ist alles sicher. Nun, zumindest solange Sie nicht unsicheres SQL in Ihre Abfrage einbinden, um beispielsweise darauf reagieren zu können, dass Sie keine Tabellennamen vorbereiten können.
Wenn ja, warum gibt es immer noch so viele erfolgreiche SQL-Injektionen? Nur weil einige Entwickler zu dumm sind, um parametrisierte Anweisungen zu verwenden?
Ja, Bildung ist hier der Hauptpunkt und die Basis des Legacy-Codes. Viele Tutorials verwenden Escape und diese können leider nicht einfach aus dem Web entfernt werden.
Ich vermeide absolute Programmierkenntnisse. Es gibt immer eine Ausnahme. Ich empfehle gespeicherte Prozeduren und Befehlsobjekte. Ein Großteil meines Hintergrunds ist SQL Server, aber ich spiele von Zeit zu Zeit mit MySQL. Gespeicherte Prozeduren bieten viele Vorteile, einschließlich zwischengespeicherter Abfragepläne. Ja, dies kann mit Parametern und Inline-SQL erreicht werden, aber das eröffnet mehr Möglichkeiten für Injection-Angriffe und hilft nicht bei der Trennung von Bedenken. Für mich ist es auch viel einfacher, eine Datenbank zu sichern, da meine Anwendungen im Allgemeinen nur Ausführungsberechtigungen für diese gespeicherten Prozeduren haben. Ohne direkten Zugriff auf Tabellen / Ansichten ist es viel schwieriger, etwas zu injizieren. Wenn der Anwendungsbenutzer gefährdet ist, hat er nur die Berechtigung, genau das auszuführen, was vordefiniert wurde.
Meine zwei Cent.
Ich würde nicht "dumm" sagen.
Ich denke, die Tutorials sind das Problem. Die meisten SQL-Tutorials, Bücher, was auch immer, erklären SQL mit Inline-Werten, ohne die Bindungsparameter zu erwähnen. Menschen, die aus diesen Tutorials lernen, haben keine Chance, es richtig zu lernen.
Da der meiste Code nicht im Hinblick auf Sicherheit und Verwaltung geschrieben wird, wählen sie fast immer die Option, wenn sie zwischen dem Hinzufügen von Funktionen (insbesondere etwas Sichtbarem, das verkauft werden kann) und Sicherheit / Stabilität / Zuverlässigkeit (was viel schwieriger zu verkaufen ist) wählen ehemalige. Sicherheit ist nur dann ein Problem, wenn sie zum Problem wird.
Kann die parametrisierte Anweisung die gesamte SQL-Injection stoppen?
Ja, solange Ihr Datenbanktreiber einen Platzhalter für jedes mögliche SQL-Literal bietet . Die meisten vorbereiteten Statement-Treiber tun dies nicht. Angenommen, Sie würden niemals einen Platzhalter für einen Feldnamen oder ein Array von Werten finden. Dadurch kann ein Entwickler mithilfe von Verkettung und manueller Formatierung wieder auf die manuelle Anpassung einer Abfrage zurückgreifen. Mit vorhergesagtem Ergebnis.
Aus diesem Grund habe ich meinen MySQL-Wrapper für PHP erstellt, der die meisten Literale unterstützt, die der Abfrage dynamisch hinzugefügt werden können, einschließlich Arrays und Bezeichner.
Wenn ja, warum gibt es immer noch so viele erfolgreiche SQL-Injektionen? Nur weil einige Entwickler zu dumm sind, um parametrisierte Anweisungen zu verwenden?
Wie Sie sehen, ist es in der Realität einfach unmöglich, alle Ihre Abfragen zu parametrisieren, selbst wenn Sie nicht dumm sind.
Zuerst meine Antwort auf Ihre erste Frage: Ja, soweit ich weiß, sind SQL-Injektionen mit parametrisierten Abfragen nicht mehr möglich. Bei Ihren folgenden Fragen bin ich mir nicht sicher und kann Ihnen nur meine Meinung zu den Gründen mitteilen:
Ich denke, es ist einfacher, die SQL-Abfragezeichenfolge "nur" zu schreiben, indem einige verschiedene Teile (möglicherweise sogar abhängig von einigen logischen Überprüfungen) zusammen mit den einzufügenden Werten verkettet werden. Es wird nur die Abfrage erstellt und ausgeführt. Ein weiterer Vorteil besteht darin, dass Sie die SQL-Abfragezeichenfolge drucken (Echo, Ausgabe oder was auch immer) und diese Zeichenfolge dann für eine manuelle Abfrage an das Datenbankmodul verwenden können.
Wenn Sie mit vorbereiteten Anweisungen arbeiten, haben Sie immer mindestens einen Schritt mehr: Sie müssen Ihre Abfrage erstellen (natürlich einschließlich der Parameter). Sie müssen die Abfrage auf dem Server vorbereiten. Sie müssen die Parameter an die tatsächlichen Werte binden, die Sie möchten für Ihre Abfrage verwenden Sie müssen die Abfrage ausführen.
Das ist etwas mehr Arbeit (und nicht so einfach zu programmieren), insbesondere für einige "schnelle und schmutzige" Jobs, die sich oft als sehr langlebig erweisen ...
Freundliche Grüße,
Box
Führen Sie die folgenden Schritte aus, um Ihre Anwendung vor SQL-Injection zu schützen:
Schritt 1. Eingabe einschränken. Schritt 2. Verwenden Sie Parameter mit gespeicherten Prozeduren. Schritt 3. Verwenden Sie Parameter mit dynamischem SQL.
Die SQL-Injection ist eine Teilmenge des größeren Problems der Code-Injection, bei der Daten und Code über denselben Kanal bereitgestellt werden und Daten mit Code verwechselt werden. Parametrisierte Abfragen verhindern dies, indem sie die Abfrage unter Verwendung des Kontexts darüber bilden, was Daten und was Code sind.
In bestimmten Fällen reicht dies nicht aus. In vielen DBMS ist es möglich, SQL mit gespeicherten Prozeduren dynamisch auszuführen, wodurch ein SQL-Injection-Fehler auf DBMS-Ebene entsteht. Das Aufrufen einer solchen gespeicherten Prozedur mithilfe parametrisierter Abfragen verhindert nicht, dass die SQL-Injection in der Prozedur ausgenutzt wird. Ein weiteres Beispiel ist in diesem Blogbeitrag zu sehen .
Häufiger verwenden Entwickler die Funktionalität falsch. Normalerweise sieht der Code ungefähr so aus, wenn er richtig gemacht wird:
db.parameterize_query("select foo from bar where baz = '?'", user_input)
Einige Entwickler verketten Zeichenfolgen miteinander und verwenden dann eine parametrisierte Abfrage, bei der die oben genannten Daten- / Codeunterscheidungen, die die gesuchten Sicherheitsgarantien bieten, nicht berücksichtigt werden:
db.parameterize_query("select foo from bar where baz = '" + user_input + "'")
Die korrekte Verwendung parametrisierter Abfragen bietet einen sehr starken, aber nicht undurchdringlichen Schutz vor SQL-Injection-Angriffen.
Selbst wenn vorbereitete Anweisungen im gesamten Code der Webanwendung ordnungsgemäß verwendet werden, können SQL-Injection-Fehler bestehen, wenn Datenbankcode-Komponenten Abfragen aus Benutzereingaben auf unsichere Weise erstellen. Das folgende Beispiel zeigt eine gespeicherte Prozedur, die für die SQL-Injection im Parameter @name anfällig ist:
CREATE PROCEDURE show_current_orders
(@name varchar(400) = NULL)
AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ‘SELECT id_num, searchstring FROM searchorders WHERE ‘ +
‘searchstring = ‘’’ + @name + ‘’’’;
EXEC (@sql)
GO
Selbst wenn die Anwendung den vom Benutzer angegebenen Namenswert auf sichere Weise an die gespeicherte Prozedur übergibt, verkettet die Prozedur dies direkt zu einer dynamischen Abfrage und ist daher anfällig.