In dieser Artikelserie werden wir umfassend diskutieren, wie SQL-Injection-Angriffe in der PHP-Entwicklungsumgebung vollständig verhindert werden können, und ein konkretes Entwicklungsbeispiel geben.
1. Einführung
PHP ist eine leistungsstarke, aber leicht zu erlernende serverseitige Skriptsprache. Selbst Programmierer mit wenig Erfahrung können damit komplexe und dynamische Websites erstellen. Allerdings gibt es oft große Schwierigkeiten, die Vertraulichkeit und Sicherheit von Internetdiensten zu gewährleisten. In dieser Artikelreihe stellen wir den Lesern den für die Webentwicklung notwendigen Sicherheitshintergrund sowie PHP-spezifisches Wissen und Code vor – Sie können damit die Sicherheit und Konsistenz Ihrer eigenen Webanwendungen schützen. Zunächst gehen wir kurz auf Serversicherheitsprobleme ein und zeigen, wie Sie in einer Shared-Hosting-Umgebung auf private Informationen zugreifen, Entwickler von Produktionsservern fernhalten, Software auf dem neuesten Stand halten, verschlüsselte Kanäle bereitstellen und den Zugriff auf Ihren Systemzugriff steuern können.
Anschließend besprechen wir weit verbreitete Schwachstellen in PHP-Skriptimplementierungen. Wir erklären Ihnen, wie Sie Ihre Skripte vor SQL-Injection schützen, Cross-Site-Scripting und Remote-Ausführung verhindern und das „Hijacking“ temporärer Dateien und Sitzungen deaktivieren.
Im letzten Artikel werden wir einen sicheren Web-Exploit implementieren. Sie erfahren, wie Sie Benutzer authentifizieren, Anwendungen autorisieren und verfolgen, Datenverluste vermeiden, risikoreiche Systembefehle sicher ausführen und Webdienste sicher nutzen. Unabhängig davon, ob Sie über ausreichende Erfahrung in der PHP-Sicherheitsentwicklung verfügen oder nicht, bietet Ihnen diese Artikelserie eine Fülle von Informationen, die Ihnen beim Erstellen sichererer Online-Anwendungen helfen.
2. Was ist SQL-Injection?
Wenn Sie vorhaben, bestimmte Daten niemals zu verwenden, macht es keinen Sinn, sie in einer Datenbank zu speichern, da die Datenbank darauf ausgelegt ist, einfach auf Daten zuzugreifen und diese zu bearbeiten in der Datenbank. Wenn Sie dies jedoch einfach tun, kann dies zu potenziellen Katastrophen führen. Diese Situation ist nicht wichtig, weil Sie selbst versehentlich alles in der Datenbank löschen könnten, sondern weil Sie, während Sie versuchen, eine „unschuldige“ Aufgabe zu erledigen, von jemandem „gekapert“ werden könnten – die Anwendung selbst hat Daten beschädigt, um Ihre eigenen zu ersetzen Daten. Wir nennen diese Substitution „Injektion“.
Tatsächlich erlauben Sie jedes Mal, wenn Sie den Benutzer um Eingaben zum Erstellen einer Datenbankabfrage bitten, dem Benutzer, an der Erstellung eines Befehls für den Zugriff auf den Datenbankserver teilzunehmen. Ein freundlicher Benutzer mag damit zufrieden sein, eine solche Kontrolle zu erlangen; ein böswilliger Benutzer wird jedoch versuchen, eine Möglichkeit zu finden, den Befehl zu verfälschen, was dazu führt, dass der verdrehte Befehl Daten löscht oder sogar etwas Gefährlicheres tut. Als Programmierer besteht Ihre Aufgabe darin, einen Weg zu finden, solche böswilligen Angriffe zu vermeiden.
3. Funktionsweise der SQL-Injection
Die Strukturierung einer Datenbankabfrage ist ein sehr unkomplizierter Prozess. Typischerweise wird es wie folgt implementiert. Um den Titel klarzustellen, gehen wir davon aus, dass Sie über eine Weindatenbanktabelle „Weine“ mit einem Feld „Sorte“ (d. h. Weintyp) verfügen:
1. Geben Sie ein Formular ein – Erlauben Benutzer können bestimmte Inhalte zur Suche einreichen. Nehmen wir an, der Benutzer sucht nach Weinen des Typs „Lagrein“.
2. Rufen Sie den Suchbegriff des Benutzers ab und behalten Sie ihn bei – indem Sie ihn einer Variablen wie dieser zuweisen:
$variety = $_POST['variety' ];
Daher ist der Wert der Variablen $variety jetzt:
lagrein
3. Wenden Sie dann die Variable in der WHERE-Klausel an. Strukturieren Sie eine Datenbankabfrage:
$query = 'SELECT * FROM wines WHERE sorte='$variety';
Der Wert der Variablen $query sieht nun also so aus:
SELECT * FROM wines WHERE sorte='lagrein'
4. Senden Sie die Abfrage an den MySQL-Server.
5. MySQL gibt alle Datensätze in der Weintabelle zurück – darunter ist der Wert des Feldsortes „Lagrein“.
Mittlerweile sollte dies ein vertrauter und einfacher Vorgang sein. Leider können Prozesse, mit denen wir vertraut und vertraut sind, manchmal leicht zu Stolzgefühlen führen. Lassen Sie uns nun die gerade erstellte Abfrage erneut analysieren.
1. Der feste Teil der von Ihnen erstellten Abfrage endet mit einem einfachen Anführungszeichen, mit dem Sie den Anfang des Variablenwerts beschreiben:
$query = ' SELECT * FROM wines WHERE sorte = '';
2. Wenden Sie den ursprünglichen festen Teil an und enthalten Sie den Wert der vom Benutzer übermittelten Variable:
$query .= $variety> 3 . Dann verbinden Sie dieses Ergebnis mit einem weiteren einfachen Anführungszeichen, das das Ende des Variablenwerts beschreibt:
$ query .= ''';
Der Wert von $query sieht also so aus dies:
SELECT * FROM wines WHERE sorte = 'lagrein'
Der Erfolg dieser Struktur hängt von der Eingabe des Benutzers ab. In diesem Beispiel verwenden Sie ein einzelnes Wort (oder möglicherweise eine Gruppe von Wörtern), um eine Weinsorte zu bezeichnen. Daher wird die Abfrage ohne Header erstellt und das Ergebnis ist das, was Sie erwarten würden – eine Liste von Weinen mit dem Weintyp „Lagrein“. Stellen wir uns nun vor, dass Ihr Benutzer anstelle einer einfachen Weinsorte vom Typ „Lagrein“ Folgendes eingibt (beachten Sie die beiden enthaltenen Satzzeichen):
lagrein ' oder 1=1;
Nun verwenden Sie weiterhin die zuvor festgelegten Teile zur Strukturierung Ihrer Abfrage (hier zeigen wir nur den Ergebniswert der $query-Variable):
SELECT * FROM wines WHERE sort = '
Anschließend verketten Sie es mit dem Wert der Variablen, die die Benutzereingabe enthält (hier fett dargestellt):
SELECT * FROM wines WHERE sorte = 'lagrein' or 1=1;
Zum Schluss , fügen Sie die unteren und oberen Anführungszeichen hinzu:
SELECT * FROM wines WHERE sorte = 'lagrein' or 1=1;'
Daher werden die Abfrageergebnisse deutlich von Ihren Erwartungen abweichen. Tatsächlich enthält Ihre Abfrage jetzt nicht nur eine, sondern zwei Anweisungen, da das letzte vom Benutzer eingegebene Semikolon die erste Anweisung (Datensatzauswahl) beendet und eine neue Anweisung gestartet hat. In diesem Fall hat die zweite Anweisung keine andere Bedeutung als ein einfaches einfaches Anführungszeichen. Die erste Anweisung ist jedoch auch nicht das, was Sie implementieren möchten. Wenn der Benutzer in der Mitte seiner Eingabe ein einfaches Anführungszeichen setzt, schaut er sich am Ende den Wert der Variablen an und führt eine weitere Bedingung ein. Anstatt also die Datensätze abzurufen, deren Sorte „Lagrein“ ist, rufen wir Datensätze ab, die eines von zwei Kriterien erfüllen (das erste gehört Ihnen und das zweite gehört ihm – Sorte ist „Lagrein“ oder 1. Das ist der Datensatz von 1). Da 1 immer 1 ist, werden alle Datensätze abgerufen!
In der Praxis mag es auf den ersten Blick mühsam erscheinen, Ihren Benutzern die Anzeige aller Datensätze und nicht nur einiger davon zu ermöglichen, aber in Wirklichkeit ist es so. Der Zugriff auf alle Datensätze kann ihm leicht Informationen über die internen Daten liefern Struktur der Tabelle und liefert ihm damit eine wichtige Referenz für die Erreichung weiterer bösartiger Ziele in der Zukunft. Die gerade beschriebene Situation wäre insbesondere dann der Fall, wenn Ihre Datenbank anstelle der scheinbar harmlosen Informationen über Alkohol beispielsweise eine Liste der Jahresverdienste der Mitarbeiter enthalten würde.
Aus theoretischer Sicht ist diese Art von Angriff tatsächlich eine schreckliche Sache. Durch das Einfügen unerwarteter Inhalte in Ihre Abfrage kann dieser Benutzer Ihren Datenbankzugriff in die Erreichung seiner eigenen Ziele umwandeln. Nun steht ihm Ihre Datenbank offen – genau wie Ihnen.
4. PHP- und MySQL-Injection
Wie wir zuvor beschrieben haben, macht PHP aufgrund seines eigenen Designs nichts Besonderes – außer, gemäß Ihren Anweisungen zu arbeiten. Daher würde es bei Verwendung durch einen böswilligen Benutzer nur auf Anfrage einem speziell konzipierten Angriff „zustimmen“ – wie dem, den wir zuvor beschrieben haben.
Wir gehen davon aus, dass Sie nicht absichtlich oder sogar versehentlich eine Datenbankabfrage mit schädlichen Folgen erstellen – wir gehen also davon aus, dass das Problem in den Eingaben Ihrer Benutzer liegt. Schauen wir uns nun die verschiedenen Möglichkeiten genauer an, wie Benutzer Ihrem Skript Informationen bereitstellen können.
5. Arten von Benutzereingaben
Heutzutage werden die Aktionen, die Benutzer ergreifen können, um Ihre Skripte zu beeinflussen, immer komplexer.
Die offensichtlichste Quelle für Benutzereingaben ist natürlich ein Texteingabefeld in einem Formular. Wenn Sie ein solches Feld verwenden, ermutigen Sie einen Benutzer im wahrsten Sinne des Wortes, beliebige Daten einzugeben. Darüber hinaus stellen Sie dem Benutzer einen großen Eingabebereich zur Verfügung; es gibt für Sie keine Möglichkeit, die Art der Daten, die ein Benutzer eingeben kann, im Voraus einzuschränken (Sie können jedoch die Länge begrenzen). Aus diesem Grund gehen die meisten Injektionsangriffe von unverteidigten Formularfeldern aus.
Es gibt jedoch auch andere Angriffsquellen, und wenn Sie einen Moment darüber nachdenken, fällt Ihnen eine Technik ein, die im Hintergrund des Formulars verborgen ist – die POST-Methode! Durch eine kurze Analyse des in der Navigationssymbolleiste des Browsers angezeigten URI kann ein aufmerksamer Benutzer leicht erkennen, welche Informationen an ein Skript übergeben werden. Obwohl solche URIs typischerweise programmgesteuert generiert werden, hindert nichts einen böswilligen Benutzer daran, einfach einen URI mit einem unpassenden Variablenwert in einen Browser einzugeben – und so auf die Möglichkeit zu lauern, eine Datenbank zu öffnen, die möglicherweise missbraucht werden könnte.
Eine gängige Strategie zur Einschränkung der Benutzereingabe besteht darin, in einem Formular ein Auswahlfeld anstelle eines Eingabefelds bereitzustellen. Diese Art der Kontrolle kann den Benutzer zwingen, aus einem vordefinierten Satz von Werten auszuwählen, und kann den Benutzer daran hindern, Inhalte einzugeben, die bis zu einem gewissen Grad nicht sichtbar sind. Aber genauso wie ein Angreifer einen URI „fälschen“ kann (d. h. einen URI erstellen, der einen vertrauenswürdigen, aber ungültigen URI nachahmt), kann er sich auch als Ihr Formular ausgeben und seine eigene Version davon erstellen und somit eine neue Version des URI erstellen das Optionsfeld „Ungültige“ anstelle vordefinierter sicherer Auswahlen anwenden. Das geht ganz einfach: Er muss sich nur den Quellcode ansehen, dann den Quellcode des Formulars ausschneiden und einfügen – und schon öffnet sich die Tür für ihn.
Nach der Änderung der Auswahl kann er das Formular absenden und seine ungültigen Befehle werden so akzeptiert, als wären es die ursprünglichen Befehle. Daher kann der Benutzer viele verschiedene Methoden verwenden, um zu versuchen, Schadcode in ein Skript einzuschleusen.
Das Obige ist einer der Inhalte des vollständigen Verbots von SQL-Injection-Angriffen in PHP Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!