Warum tritt SQL Injection bei dieser Abfrage in einer gespeicherten Prozedur nicht auf?

18

Ich habe die folgende gespeicherte Prozedur erstellt:

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

Jetzt habe ich versucht, so etwas zu tun. Vielleicht mache ich das falsch, aber ich möchte sicher sein, dass eine solche Prozedur jede SQL-Injection verhindern kann:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

Die folgende Abbildung zeigt, dass die oben genannte SQL in SSMS ausgeführt wird und die Ergebnisse anstelle eines Fehlers korrekt angezeigt werden:

Bildbeschreibung hier eingeben

Übrigens habe ich diesen Teil nach dem Semikolon hinzugefügt, nachdem die Abfrage ausgeführt wurde. Dann habe ich es erneut ausgeführt, aber als ich überprüft habe, ob die Tabelle tblActor existiert oder nicht, war sie immer noch da. Mache ich etwas falsch? Oder ist das wirklich spritzwassergeschützt? Ich denke, was ich hier zu fragen versuche, ist auch, dass eine gespeicherte Prozedur wie diese sicher ist? Vielen Dank.

Ravi
quelle
Haben Sie dies versuchtEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK

Antworten:

38

Dieser Code funktioniert ordnungsgemäß, weil er:

  1. Parametriert und
  2. Keine Dynamic SQL

Damit SQL Injection zu arbeiten, müssen Sie eine Abfrage - String bauen (die Sie nicht tun werden) und nicht übersetzen einzelne Apostrophe ( ') in entkam-Apostrophe ( '') (die über die Eingangsparameter entkam).

Bei dem Versuch, einen "kompromittierten" Wert einzugeben, handelt es sich bei der 'Male; DROP TABLE tblActor'Zeichenfolge lediglich um eine einfache Zeichenfolge.

Nun, wenn Sie etwas in der Art von:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = '
          + @InputParam;

EXEC(@SQL);

dann , dass wäre anfällig für SQL - Injection , da die Abfrage nicht in dem aktuellen ist, vorab geparsten Kontext; Diese Abfrage ist im Moment nur eine weitere Zeichenfolge. Der Wert von @InputParamkönnte also '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;ein Problem darstellen, da diese Abfrage wie folgt gerendert und ausgeführt wird:

SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;

Dies ist einer von mehreren Hauptgründen für die Verwendung von Stored Procedures: von Natur aus sicherer (vorausgesetzt, Sie umgehen diese Sicherheit nicht, indem Sie Abfragen wie oben gezeigt erstellen, ohne die Werte der verwendeten Parameter zu validieren). Wenn Sie jedoch dynamisches SQL erstellen müssen, sollten Sie dies auch folgendermaßen parametrisieren sp_executesql:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';

EXEC sp_executesql
  @SQL,
  N'SomeDate_tmp DATETIME',
  @SomeDate_tmp = @InputParam;

Bei Verwendung dieses Ansatzes würde jemand, der versucht, '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;einen DATETIMEEingabeparameter zu übergeben, beim Ausführen der gespeicherten Prozedur einen Fehler erhalten. Oder selbst wenn die gespeicherte Prozedur @InputParameterals akzeptiert NVARCHAR(100)würde, müsste sie in eine konvertiert DATETIMEwerden, um in diesen sp_executesqlAufruf überzugehen. Und selbst wenn der Parameter in Dynamic SQL ein Zeichenfolgentyp ist, wird bei der gespeicherten Prozedur in erster Linie jedes einzelne Apostroph automatisch zu einem doppelten Apostroph maskiert.

Es gibt eine weniger bekannte Angriffsart, bei der der Angreifer versucht, das Eingabefeld mit Apostrophen auszufüllen, sodass eine Zeichenfolge in der gespeicherten Prozedur, die zum Erstellen von Dynamic SQL verwendet werden würde, jedoch als zu klein deklariert wird, nicht für alles passen kann und schiebt den abschließenden Apostroph heraus und endet irgendwie mit der richtigen Anzahl von Apostrophen, um nicht länger innerhalb der Zeichenkette "entkommen" zu werden. Dies wird als SQL-Kürzung bezeichnet und wurde in einem Artikel des MSDN-Magazins mit dem Titel "Neue SQL-Kürzungsangriffe und ihre Vermeidung" von Bala Neerumalla behandelt. Der Artikel ist jedoch nicht mehr online. Die Ausgabe mit diesem Artikel - die Ausgabe vom November 2006 des MSDN Magazine - ist nur als Windows-Hilfedatei (in .chmFormat). Wenn Sie es herunterladen, wird es möglicherweise aufgrund der Standardsicherheitseinstellungen nicht geöffnet. In diesem Fall klicken Sie mit der rechten Maustaste auf die Datei MSDNMagazineNovember2006en-us.chm und wählen Sie "Eigenschaften". In einer dieser Registerkarten gibt es eine Option für "Vertraue diesem Dateityp" (oder so ähnlich), die überprüft / aktiviert werden muss. Klicken Sie auf die Schaltfläche "OK" und versuchen Sie dann erneut, die CHM- Datei zu öffnen .

Eine weitere Variante des Truncation-Angriffs ist die Annahme, dass eine lokale Variable verwendet wird, um den vom Benutzer angegebenen "sicheren" Wert zu speichern, da einzelne Anführungszeichen so verdoppelt wurden, dass sie ausgeblendet werden, um diese lokale Variable aufzufüllen und das einzelne Anführungszeichen zu platzieren Am Ende. Die Idee dabei ist, dass, wenn die lokale Variable nicht die richtige Größe hat, am Ende nicht genügend Platz für das zweite einfache Anführungszeichen vorhanden ist. Lassen Sie die Variable mit einem einfachen Anführungszeichen enden, das dann mit dem einfachen Anführungszeichen kombiniert wird Beendet den Literalwert in Dynamic SQL und wandelt dieses endende einfache Anführungszeichen in ein eingebettetes einfaches Anführungszeichen mit Escapezeichen um. Das Zeichenfolgenliteral in Dynamic SQL endet dann mit dem nächsten einfachen Anführungszeichen, mit dem das nächste Zeichenfolgenliteral beginnen sollte. Beispielsweise:

-- Parameters:
DECLARE @UserID      INT = 37,
        @NewPassword NVARCHAR(15) = N'Any Value ....''',
        @OldPassword NVARCHAR(15) = N';Injected SQL--';

-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
        @NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
        @OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');

SELECT @NewPassword AS [@NewPassword],
       REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
       @NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword          REPLACE output          @NewPassword_fixed
Any Value ....'       Any Value ....''        Any Value ....'
*/

SELECT @OldPassword AS [@OldPassword],
       REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
       @OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword          REPLACE output          @OldPassword_fixed
;Injected SQL--       ;Injected SQL--         ;Injected SQL--
*/

SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
           + @NewPassword_fixed + N''' WHERE [TableNameID] = '
           + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
           + @OldPassword_fixed + N''';';

SELECT @SQL AS [Injected];

Hier ist nun das auszuführende Dynamic SQL:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Dasselbe dynamische SQL in einem besser lesbaren Format lautet:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';

Injected SQL--';

Dies zu beheben ist einfach. Führen Sie einfach einen der folgenden Schritte aus:

  1. DO NOT USE DYNAMIC SQL WENN NICHT UNBEDINGT ERFORDERLICH! (Ich liste dies zuerst auf, weil es wirklich das erste sein sollte, was zu berücksichtigen ist).
  2. Ordnungsgemäße Größe der lokalen Variablen (dh sie sollte doppelt so groß sein wie der Eingabeparameter), nur für den Fall, dass alle übergebenen Zeichen einfache Anführungszeichen sind.
  3. Verwenden Sie keine lokale Variable, um den "festen" Wert zu speichern. stecke das einfach REPLACE()direkt in die Erstellung des Dynamic SQL:

    SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
               + REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
               + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
               + REPLACE(@OldPassword, N'''', N'''''') + N''';';
    
    SELECT @SQL AS [No SQL Injection here];

    Das Dynamic SQL ist nicht mehr gefährdet:

    UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Anmerkungen zum obigen Funktionsbeispiel:

  1. Ja, das ist ein sehr ausgeklügeltes Beispiel. Es gibt nicht viel, was man mit nur 15 Zeichen tun kann. Sicher, vielleicht DELETE tableNameum destruktiv zu sein, aber weniger wahrscheinlich, um einen Back-Door-Benutzer hinzuzufügen oder ein Administratorkennwort zu ändern.
  2. Diese Art von Angriff erfordert wahrscheinlich die Kenntnis des Codes, der Tabellennamen usw. Es ist weniger wahrscheinlich, dass ein zufälliger Fremder / Script-Kiddie sie ausführt, aber ich habe an einem Ort gearbeitet, an dem ein eher verärgerter ehemaliger Mitarbeiter angegriffen wurde, der von einer Sicherheitslücke wusste auf einer bestimmten Webseite, die niemand sonst kannte. Das heißt, manchmal haben Angreifer eine genaue Kenntnis des Systems.
  3. Sicher, das Zurücksetzen des Kennworts für jeden wird wahrscheinlich untersucht, was das Unternehmen darauf hinweisen kann, dass ein Angriff stattfindet, aber dennoch genügend Zeit bietet, um einen Back-Door-Benutzer einzuschleusen oder sekundäre Informationen für die spätere Verwendung / Nutzung abzurufen.
  4. Auch wenn dieses Szenario größtenteils akademisch ist (dh in der realen Welt wahrscheinlich nicht vorkommt), ist es immer noch nicht unmöglich.

Ausführlichere Informationen zu SQL Injection (in Bezug auf verschiedene RDBMS und Szenarien) finden Sie im Open Web Application Security Project (OWASP) unter:
Testen auf SQL Injection

Verwandte Antwort auf Stapelüberlauf bei SQL Injection und SQL Truncation:
Wie sicher ist T-SQL, nachdem Sie das Escape-Zeichen ersetzt haben?

Solomon Rutzky
quelle
2
Oh, vielen Dank, das ist eine großartige Antwort. Ich verstehe jetzt. Ich würde die Technik, die Sie am Ende erwähnt haben, auch gerne sehen, wenn der Angreifer versucht, das Eingabefeld mit Apostrophen auszufüllen, sofern Sie diese finden. Danke im Voraus. Ich werde dies offen halten, falls Sie es nicht finden, werde ich dies als Antwort wählen.
Ravi
1
@ Ravi Ich habe den Link gefunden, aber er gelangt nicht mehr zum Artikel, da alle jetzt archiviert sind. Ich habe jedoch einige Informationen und nützliche Links hinzugefügt und versuche immer noch, den Artikel in diesen Archiven zu finden.
Solomon Rutzky
1
Vielen Dank, Srutzsky. Ich werde den OWASP-Artikel und die Tests zur Injektion lesen. Wenn ich mich richtig erinnere, verfügt 'mutillidae', die anfällige Web-App für Sicherheitstests, über eine SQL-Injection, die ich am College mit der Zeichenfolge 'OR 1 = 1' durchgeführt habe. Dies führte dazu, dass ich mich bei der Web-App als Administrator I anmeldete denken. Zu diesem Zeitpunkt wurde ich zum ersten Mal in die SQL-Injection eingeführt.
Ravi
1
Ich kann die CHM-Datei auch nicht anzeigen, aber ich danke Ihnen für diese perfekte Antwort und für alle hilfreichen Links, einschließlich der von stackoverflow und der von OWASP. Ich habe es heute gelesen und viel daraus gelernt.
Ravi
2

Die einfache Sache ist, dass Sie Daten überhaupt nicht mit Befehlen verwechseln. Die Werte für die Parameter werden niemals als Teil des Befehls behandelt und daher niemals ausgeführt.

Ich habe darüber gebloggt unter: http://blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/

Rob Farley
quelle
Vielen Dank, Rob. Ich habe dieses Buch zum späteren Lesen vorgemerkt.
Ravi