Wie kann die Hygiene, die einfachen Anführungszeichen entgeht, durch SQL-Injection in SQL Server beseitigt werden?

71

Zu Beginn bin ich mir bewusst, dass parametrisierte Abfragen die beste Option sind, aber ich frage mich, was die unten dargestellte Strategie anfällig macht. Die Leute bestehen darauf, dass die unten stehende Lösung nicht funktioniert, deshalb suche ich nach einem Beispiel dafür, warum dies nicht der Fall ist.

Welche Art von Injection kann dies verhindern, wenn dynamisches SQL mit dem folgenden Escapezeichen in Code eingebaut wird, bevor es an einen SQL Server gesendet wird?

string userInput= "N'" + userInput.Replace("'", "''") + "'"

Eine ähnliche Frage wurde hier beantwortet , aber ich glaube nicht, dass eine der Antworten hier zutreffend ist.

In SQL Server ist es nicht möglich, das einfache Anführungszeichen mit einem "\" zu versehen.

Ich glaube, SQL-Schmuggel mit Unicode ( hier beschrieben ) würde durch die Tatsache vereitelt, dass die erzeugte Zeichenfolge durch das N vor dem einfachen Anführungszeichen als Unicode markiert wird. Soweit ich weiß, gibt es keine anderen Zeichensätze, die SQL Server automatisch in ein einfaches Anführungszeichen übersetzt. Ohne ein einfaches Zitat glaube ich nicht, dass eine Injektion möglich ist.

Ich glaube auch nicht, dass String Truncation ein brauchbarer Vektor ist. SQL Server wird das Abschneiden sicherlich nicht durchführen, da die maximale Größe für a laut Microsoftnvarchar 2 GB beträgt . Eine 2-GB-Zeichenfolge ist in den meisten Situationen nicht und in meinen nicht möglich.

Eine Injektion zweiter Ordnung könnte möglich sein, aber ist es möglich, wenn:

  1. Alle Daten, die in die Datenbank gelangen, werden mit der oben beschriebenen Methode bereinigt
  2. Werte aus der Datenbank werden niemals an dynamisches SQL angehängt (warum sollten Sie das überhaupt tun, wenn Sie nur auf den Tabellenwert im statischen Teil einer dynamischen SQL-Zeichenfolge verweisen können?).

Ich behaupte nicht, dass dies besser als oder eine Alternative zur Verwendung parametrisierter Abfragen ist, aber ich möchte wissen, wie anfällig das, was ich skizziert habe, ist. Irgendwelche Ideen?

GBleaney
quelle
1
Nein. Sie sind immer noch anfällig für Angriffe in der Form: "SELECT * FROM MyTable WHERE Field = " + userInputwann userInputist 0; DROP TABLE OhNo;.
Yuck
10
Das macht keinen Sinn. Im obigen Beispiel würde Ihre Benutzereingabe auf N'0 bereinigt. TROPFENTABELLE Oh nein; ' bevor sie jemals hingerichtet werden.
GBleaney
3
Dies gilt nur für die Bereinigung von Zeichenfolgenvariablen. Dinge wie "int" müssen nicht bereinigt werden, wenn sie als int umgewandelt werden, bevor sie der Abfrage hinzugefügt werden. Unabhängig davon frage ich gerade nur nach der Desinfektion von Saiten. Auch hier besteht keine Notwendigkeit unhöflich. Wenn Sie sich vorstellen können, dass dies nicht sicher ist, würde ich es gerne wissen.
GBleaney
Ok, wie wäre es mit userinput = "test '; drop table ohno; print'"
Kenneth Fisher
3
Wäre dies nicht eine gute Wahl, wenn dynamisches SQL unvermeidbar ist?
GBleaney

Antworten:

46

Es gibt einige Fälle, in denen diese Escape-Funktion fehlschlägt. Das offensichtlichste ist, wenn kein einfaches Anführungszeichen verwendet wird:

string table= "\"" + table.Replace("'", "''") + "\""
string var= "`" + var.Replace("'", "''") + "`"
string index= " " + index.Replace("'", "''") + " "
string query = "select * from `"+table+"` where name=\""+var+"\" or id="+index

In diesem Fall können Sie mit einem doppelten Anführungszeichen, einem Back-Tick, "ausbrechen". Im letzten Fall gibt es nichts, 1 union select password from users--woraus man "ausbrechen" kann, sodass Sie einfach schreiben können oder welche SQL-Nutzlast der Angreifer wünscht.

Die nächste Bedingung, bei der diese Escape-Funktion fehlschlägt, ist, wenn eine Unterzeichenfolge nach dem Escapezeichen der Zeichenfolge verwendet wird (und ja, ich habe solche Sicherheitslücken in freier Wildbahn gefunden):

string userPassword= userPassword.Replace("'", "''")
string userName= userInput.Replace("'", "''")
userName = substr(userName,0,10)
string query = "select * from users where name='"+userName+"' and password='"+userPassword+"'";

In diesem Falle ein Benutzername abcdefgji'wird in umgewandelt werden abcdefgji''durch die Escape - Funktion und dann verwandelte sich in zurück , abcdefgji'indem sie die Unterkette. Dies kann ausgenutzt werden, indem der Kennwortwert auf eine beliebige SQL-Anweisung gesetzt wird. In diesem Fall wird or 1=1--dies als SQL und der Benutzername als interpretiert abcdefgji'' and password=. Die resultierende Abfrage lautet wie folgt:

select * from users where name='abcdefgji'' and password=' or 1=1-- 

T-SQL und andere fortschrittliche SQL-Injektionstechniken wurden bereits erwähnt. Erweiterte SQL-Injection in SQL Server-Anwendungen ist ein großartiges Dokument, das Sie lesen sollten, wenn Sie es noch nicht getan haben.

Das letzte Problem sind Unicode-Angriffe. Diese Klasse von Sicherheitslücken tritt auf, weil die Escape-Funktion keine Multi-Byte-Codierung kennt und dies von einem Angreifer verwendet werden kann, um das Escape-Zeichen zu "verbrauchen" . Das Voranstellen eines "N" an die Zeichenfolge hilft nicht, da dies den Wert von Mehrbyte-Zeichen später in der Zeichenfolge nicht beeinflusst. Diese Art von Angriff ist jedoch sehr selten, da die Datenbank so konfiguriert werden muss, dass GBK-Unicode-Zeichenfolgen akzeptiert werden (und ich bin nicht sicher, ob MS-SQL dies kann).

Code-Injection zweiter Ordnung ist weiterhin möglich. Dieses Angriffsmuster wird durch Vertrauen in vom Angreifer kontrollierte Datenquellen erstellt. Escaping wird verwendet, um Steuerzeichen als Zeichenliteral darzustellen. Wenn die Entwickler vergessen einen Wert von a erhalten zu entkommen selectund verwendet dann diesen Wert in einer anderen Abfrage dann bam die Angreifer einen Zeichenliteral Apostroph zur Verfügung stehen werden.

Teste alles, vertraue nichts.

Turm
quelle
3
Obwohl ich damit einverstanden bin, dass es schlecht ist, mit der Sicherheit zu spielen, anstatt die Abfrage zu parametrisieren, hat er, wenn Sie seine Frage sorgfältig lesen, mehrere Schritte zur "Gewährleistung" der Sicherheit notiert. 1) Er stellt sicher, dass ein numerischer Wert tatsächlich ein numerischer Wert ist, sodass die ID-Injektion in Ihrem Beispiel fehlschlägt. 2) Er fragt nach SQL Server, der nur 'als Zeichenfolgenbegrenzer akzeptiert . Die Verwendung von Häkchen oder doppeltem Anführungszeichen würde dem Angreifer nicht helfen
Nikola Radosavljević
@Nikola Radosavljević ja, aber Back-Tics und fehlende Anführungszeichen können immer noch ein Problem sein, je nachdem, wie er diesen Check implantiert hat. Dieser Beitrag ist eindeutig vom Standpunkt eines Angreifers und nicht eines Verteidigers. Die Verteidigung ist bekannt und uninteressant.
Turm
3
Der Code in der Frage ist nicht anfällig für Angriffe. Dies ist eine gute Antwort als allgemeine Anleitung zum Entkommen von String-Literalen und sowohl korrekt als auch interessant. Die aufgeführten Szenarien können jedoch die Codezeile in der ursprünglichen Frage nicht beeinträchtigen. Bei Angriffen aus zweiter Hand kann es vorkommen, dass der Text ursprünglich mit einem Parameter oder als Literal eingefügt wurde, wenn er ein Anführungszeichen enthält.
Rob
18

Mit einigen zusätzlichen Bestimmungen ist Ihr oben beschriebener Ansatz nicht anfällig für SQL-Injection. Der wichtigste Angriffsvektor ist der SQL-Schmuggel. SQL-Schmuggel tritt auf, wenn ähnliche Unicode-Zeichen auf unerwartete Weise übersetzt werden (z. B. "Ändern in"). Es gibt mehrere Stellen, an denen ein Anwendungsstapel für SQL Smuggling anfällig sein kann.

  • Behandelt die Programmiersprache Unicode-Zeichenfolgen angemessen? Wenn die Sprache nicht Unicode-fähig ist, kann sie ein Byte in einem Unicode-Zeichen als einfaches Anführungszeichen falsch identifizieren und es umgehen.

  • Behandelt die Client-Datenbankbibliothek (z. B. ODBC usw.) Unicode-Zeichenfolgen ordnungsgemäß? System.Data.SqlClient im .Net-Framework funktioniert, aber wie steht es mit alten Bibliotheken aus der Windows 95-Ära? ODBC-Bibliotheken von Drittanbietern existieren tatsächlich. Was passiert, wenn der ODBC-Treiber keinen Unicode in der Abfragezeichenfolge unterstützt?

  • Behandelt die DB die Eingabe korrekt? Moderne Versionen von SQL sind immun, wenn Sie N '' verwenden, aber was ist mit SQL 6.5? SQL 7.0? Mir sind keine besonderen Sicherheitslücken bekannt, aber dies war in den 90er Jahren für Entwickler nicht auf dem Radar.

  • Puffer läuft über? Ein weiteres Problem ist, dass die Zeichenfolge in Anführungszeichen länger als die ursprüngliche Zeichenfolge ist. In welcher Version von SQL Server wurde das 2-GB-Limit für die Eingabe eingeführt? Was war vorher die Grenze? Was ist bei älteren SQL-Versionen passiert, wenn eine Abfrage das Limit überschritten hat? Gibt es Einschränkungen für die Länge einer Abfrage aus Sicht der Netzwerkbibliothek? Oder auf die Länge der Zeichenfolge in der Programmiersprache?

  • Gibt es Spracheinstellungen, die sich auf den in der Funktion Replace () verwendeten Vergleich auswirken? .Net führt immer einen binären Vergleich für die Funktion Replace () durch. Wird das immer so sein? Was passiert, wenn eine zukünftige Version von .NET das Überschreiben dieses Verhaltens auf der Ebene app.config unterstützt? Was wäre, wenn wir anstelle von Replace () einen regulären Ausdruck verwenden würden, um ein einfaches Anführungszeichen einzufügen? Beeinflussen die Gebietsschemaeinstellungen des Computers diesen Vergleich? Wenn eine Verhaltensänderung aufgetreten ist, ist sie möglicherweise nicht anfällig für SQL-Injection. Möglicherweise wurde die Zeichenfolge jedoch versehentlich bearbeitet, indem ein Unicode-Zeichen, das wie ein einfaches Anführungszeichen aussah, in ein einfaches Anführungszeichen geändert wurde, bevor es jemals die Datenbank erreichte.

Angenommen, Sie verwenden die Funktion System.String.Replace () in C # in der aktuellen Version von .Net mit der integrierten SqlClient-Bibliothek für eine aktuelle Version von SQL Server (2005-2012) anfällig. Wenn Sie anfangen, Dinge zu ändern, können keine Versprechungen gemacht werden. Der parametrisierte Abfrageansatz ist der richtige Ansatz für Effizienz, Leistung und (in einigen Fällen) Sicherheit.

WARNUNG Die obigen Kommentare sind keine Bestätigung dieser Technik. Es gibt mehrere andere sehr gute Gründe, warum dies der falsche Ansatz zum Generieren von SQL ist. Ihre Detaillierung liegt jedoch außerhalb des Rahmens dieser Frage.

VERWENDEN SIE DIESE TECHNIK NICHT FÜR NEUE ENTWICKLUNGEN.

VERWENDEN SIE DIESE TECHNIK NICHT FÜR NEUE ENTWICKLUNGEN.

VERWENDEN SIE DIESE TECHNIK NICHT FÜR NEUE ENTWICKLUNGEN.

StrayCatDBA
quelle
Wenn nicht diese Technik, wofür sollte ich sie dann CREATE LOGINbei Neuentwicklungen verwenden?
Binki
9

Die Verwendung von Abfrageparametern ist besser, einfacher und schneller als das Anhalten von Anführungszeichen.


In Bezug auf Ihren Kommentar sehe ich, dass Sie die Parametrisierung anerkannt haben, aber sie verdient Hervorhebung. Warum sollten Sie Escape verwenden, wenn Sie parametrisieren können?

In Advanced SQL Injection Suchen Sie in SQL Server-Anwendungen nach dem Wort "Ersetzen" im Text und lesen Sie ab diesem Zeitpunkt einige Beispiele, in denen Entwickler versehentlich SQL-Injection-Angriffe zugelassen haben, selbst nachdem sie Benutzereingaben entgangen sind.


Es gibt einen Randfall, in dem das Entkommen von Anführungszeichen \zu einer Sicherheitsanfälligkeit führt, da \in einigen Zeichensätzen die Hälfte eines gültigen Mehrbyte-Zeichens wird. Dies gilt jedoch nicht für Ihren Fall, da \es sich nicht um den entkommenden Charakter handelt.

Wie andere bereits erwähnt haben, fügen Sie Ihrer SQL möglicherweise auch dynamischen Inhalt für etwas anderes als ein Zeichenfolgenliteral oder ein Datumsliteral hinzu. Tabellen- oder Spaltenbezeichner werden "in SQL oder [ ]in Microsoft / Sybase durch begrenzt. SQL-Schlüsselwörter haben natürlich keine Trennzeichen. In diesen Fällen empfehle ich, die zu interpolierenden Werte auf die Whitelist zu setzen.

Fazit ist , dass Flucht ist eine wirksame Verteidigung, wenn Sie sicherstellen können , dass Sie es konsequent tun. Das ist das Risiko: Einer der Entwickler Ihrer Anwendung kann einen Schritt weglassen und eine String-Interpolation unsicher durchführen.

Gleiches gilt natürlich auch für andere Methoden wie die Parametrisierung. Sie sind nur dann wirksam, wenn Sie sie konsequent ausführen. Aber ich finde es einfacher und schneller, Parameter zu verwenden, als die richtige Art der Flucht herauszufinden. Entwickler verwenden eher eine Methode, die praktisch ist und sie nicht verlangsamt.

Bill Karwin
quelle
3
Wie ich in der ersten Zeile meines Beitrags feststelle, bin ich mir dessen sehr wohl bewusst. Ich frage nur, ob es einen bestimmten Grund gibt, warum das, was ich skizziert habe, nicht sicher ist.
GBleaney
1
Das ist eine großartige Ressource, aber die dort vorgeschlagenen möglichen Probleme sind hier nicht wirklich ein Problem. Erstens ist das Erstellen von Anführungszeichen mit der Methode "char (0x63)" nutzlos, da diese Anweisung als Zeichenfolge interpretiert und nicht ausgeführt wird. Was die SQL-Injektion zweiter Ordnung betrifft, habe ich dies bereits im ursprünglichen Beitrag angesprochen.
GBleaney
1
"Warum sollten Sie Escape verwenden, wenn Sie parametrisieren könnten?" Manchmal ist es sehr schwierig, Legacy-Code für parametrisierte Abfragen nachzurüsten.
RJ Dunnill
@RJDunnill, Nach meiner Erfahrung ist das Ändern des Codes nicht der schwierige Teil, selbst für Legacy-Code. Bei meinem letzten Job habe ich eine ganze App aktualisiert, um parametrisierte SQL-Abfragen in ungefähr 4 Stunden zu verwenden. Die größere Schwierigkeit besteht darin, dass niemand weiß, wie er auf den Code zugreifen, ihn erstellen oder bereitstellen kann. Aber das ist in diesem Fall nicht die Frage. Wir gehen davon aus, dass es sich bei der Frage um ein Projekt handelt, bei dem der Code bereits aktiv entwickelt wird, und nicht um Code, der verloren gegangen ist.
Bill Karwin
4

SQL-Injection tritt auf, wenn vom Benutzer bereitgestellte Eingaben als Befehle interpretiert werden. Hier bedeutet Befehl alles, was nicht als erkanntes Datentypliteral interpretiert wird .

Wenn Sie die Benutzereingabe nur in Datenliteralen verwenden, insbesondere nur in Zeichenfolgenliteralen, wird die Benutzereingabe nur dann als etwas anderes als Zeichenfolgendaten interpretiert, wenn sie den Zeichenfolgenliteralkontext verlassen kann. Bei Zeichenfolgen- oder Unicode-Zeichenfolgenliteralen ist es das einfache Anführungszeichen, das die Literaldaten einschließt, während eingebettete einfache Anführungszeichen mit zwei einfachen Anführungszeichen dargestellt werden müssen.

Um einen String-Literal-Kontext zu verlassen, müsste ein einzelnes einfaches Anführungszeichen (sic) angegeben werden, da zwei einfache Anführungszeichen als String-Literal-Daten und nicht als String-Literal-Endbegrenzer interpretiert werden.

Wenn Sie also ein einzelnes Anführungszeichen in den vom Benutzer angegebenen Daten durch zwei einfache Anführungszeichen ersetzen, kann der Benutzer den Zeichenfolgenliteral-Kontext nicht verlassen.

Gumbo
quelle
4

SQL Injection kann über Unicode erfolgen. Wenn die Web-App eine URL wie diese hat:

http: // mywebapp / widgets /? Code = ABC

Dies erzeugt SQL wie select * aus Widgets, wobei Code = 'ABC'.

aber ein Hacker betritt dies:

http: // mywebapp / widgets /? Code = ABC% CA% BC; Tabellen-Widgets löschen--

Das SQL sieht so aus, als würde es * aus Widgets auswählen, wobei Code = 'ABC'; Tabellen-Widgets löschen-- '

und SQL Server führt zwei SQL-Anweisungen aus. Eine zum Auswählen und eine zum Löschen. Ihr Code konvertiert wahrscheinlich das URL-codierte% CA% BC in den Unicode U02BC, bei dem es sich um einen "Modifikator-Buchstaben-Apostroph" handelt. Die Ersetzungsfunktion in .Net behandelt dies NICHT als einfaches Anführungszeichen. Microsoft SQL Server behandelt es jedoch wie ein einfaches Anführungszeichen. Hier ist ein Beispiel, das wahrscheinlich SQL Injection ermöglicht:

string badValue = ((char)0x02BC).ToString();
badValue = badValue + ";delete from widgets--";
string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''");
TestTheSQL(sql);
Rob Kraft
quelle
Dies funktioniert: string sql = "SELECT * FROM WIDGETS WHERE ID = '" + badValue.Replace ("'", "''") + "'";
Linjunhalida
2

Es gibt wahrscheinlich keinen 100% sicheren Weg, wenn Sie eine Zeichenfolgenverkettung durchführen. Sie können versuchen, den Datentyp für jeden Parameter zu überprüfen. Wenn alle Parameter eine solche Validierung bestehen, fahren Sie mit der Ausführung fort. Wenn Ihr Parameter beispielsweise vom Typ int sein sollte und Sie etwas erhalten, das nicht in int konvertiert werden kann, lehnen Sie es einfach ab.

Dies funktioniert jedoch nicht, wenn Sie nvarchar-Parameter akzeptieren.

Wie andere bereits betonten. Am sichersten ist die Verwendung einer parametrisierten Abfrage.

George Wesley
quelle