1. Erstellen Sie eine Sicherheitsabstraktionsschicht
Wir empfehlen nicht, die oben beschriebenen Techniken manuell auf jede Instanz von Benutzereingaben anzuwenden, wir empfehlen jedoch dringend, zu diesem Zweck eine Abstraktionsschicht zu erstellen. Eine einfache Abstraktion besteht darin, Ihren Validierungsplan einer Funktion hinzuzufügen und diese Funktion für jedes vom Benutzer eingegebene Element aufzurufen. Natürlich können wir auch eine komplexere Abstraktion auf höherer Ebene erstellen – indem wir eine sichere Abfrage in eine Klasse kapseln, die von allen Anwendungen verwendet werden kann. Es gibt viele solcher vorgefertigten kostenlosen Kurse, die online verfügbar sind. In diesem Artikel werden wir einige davon besprechen.
Diese Abstraktion bietet mindestens drei Vorteile (und jeder davon verbessert das Sicherheitsniveau):
1. Lokalisierter Code.
2. Machen Sie die Abfragestruktur schneller und zuverlässiger – denn dadurch kann ein Teil der Arbeit auf abstrakten Code verlagert werden.
3. Wenn dies unter Berücksichtigung von Sicherheitsfunktionen entwickelt und entsprechend angewendet wird, werden die verschiedenen Injektionsangriffe, die wir zuvor besprochen haben, wirksam verhindert.
2. Bestehende Anwendungen verbessern
Wenn Sie eine bestehende Anwendung verbessern möchten, ist es am besten, eine einfache Abstraktionsschicht zu verwenden. Eine Funktion, die einfach alle von Ihnen gesammelten Benutzereingaben „bereinigt“, könnte wie folgt aussehen:
function safe( $string ) {
return ''' }
【Hinweis】Wir haben einfache Anführungszeichen erstellt, die Wertanforderungen und der Funktion mysql_real_escape_string() entsprechen. Als nächstes können Sie diese Funktion verwenden, um eine $query-Variable wie folgt zu erstellen:
$variety = sicher( $_POST['variety'] );
$query = ' SELECT * FROM wines WHERE sorte =' . $variety;
Nun versucht Ihr Benutzer, einen Injektionsangriff durchzuführen – indem er Folgendes als Wert der Variablen $variety eingibt:
lagrein' oder 1=1;
Hinweis dass, wenn die obige „Reinigung“ nicht durchgeführt wird, die endgültige Abfrage wie folgt aussieht (dies wird zu unvorhersehbaren Ergebnissen führen):
SELECT * FROM wines WHERE sorte = 'lagrein' or 1= 1;'
Aber jetzt, da die Benutzereingaben gelöscht wurden, hat die Abfrageanweisung die folgende harmlose Form:
SELECT * FROM wines WHERE sorte = 'lagrein' or 1 =1;'
Wenn Sie eine neue Anwendung erstellen, können Sie eine Sicherheitsabstraktionsschicht von Grund auf erstellen. Die neu verbesserte Unterstützung von PHP 5 für MySQL (die sich hauptsächlich in der neuen MySQLi-Erweiterung widerspiegelt) bietet nun starke Unterstützung für diese Sicherheitsfunktion (sowohl prozedural als auch objektorientiert). Informationen zu MySQL erhalten Sie auf der Website http://php.net/mysqli. Beachten Sie, dass diese MySQLi-Unterstützung nur verfügbar ist, wenn Sie PHP mit der Option --with-mysqli=path/to/mysql_config kompilieren. Hier ist eine prozedurale Version dieses Codes, die zum Sichern einer MySQL-basierten Abfrage verwendet wird:
<?php
//Benutzereingaben abrufen
$animalName = $_POST['animalName'];
//Mit der Datenbank verbinden
$connect = mysqli_connect( ' localhost ', 'username', 'password', 'database' );
if ( !$connect ) exit( 'connection failed: ' . mysqli_connect_error() );
//Eine Abfrageanweisungsquelle erstellen
$stmt = mysqli_prepare( $connect,'SELECT Intelligence FROM Animals WHERE Name = ?' );
if ( $stmt ) {
//Binde den Austausch an die Anweisung
mysqli_stmt_bind_param( $stmt, 's ', $animalName );
//Anweisung ausführen
mysqli_stmt_execute( $stmt );
//Ergebnisse abrufen...
mysqli_stmt_bind_result( $stmt, $intelligence ); und zeige es an
if ( mysqli_stmt_fetch( $stmt ) ) {
print 'A $animalName has $intelligence Intelligence.n';
} else {
print 'Leider wurden keine Datensätze gefunden.';
}
//Anweisungsquelle löschen
mysqli_stmt_close( $stmt );
}
mysqli_close( $connect );
?>
Die MySQLi-Erweiterung Eine Reihe von Funktionen ist zur Strukturierung und Ausführung von Abfragen bereitgestellt. Darüber hinaus bietet es auch sehr genau die Funktionalität, die zuvor mit unserer eigenen Funktion „safe()“ erreicht wurde.
Im obigen Snippet werden zunächst die vom Benutzer übermittelten Eingabeinhalte gesammelt und die Datenbankverbindung hergestellt. Verwenden Sie dann die Funktion mysqli_prepare(), um eine Abfragequelle zu erstellen – hier mit dem Namen $stmt, um den Namen der Funktion wiederzugeben, die sie verwendet. Diese Funktion benötigt zwei Parameter: die Verbindungsressource und eine Zeichenfolge (das „?“-Zeichen wird jedes Mal eingefügt, wenn Sie einen Wert mithilfe der Erweiterung einfügen). In diesem Fall haben Sie nur einen solchen Wert – den Namen des Tieres.
Beachten Sie, dass in einer SELECT-Anweisung die einzige gültige Stelle für die Platzierung des „?“ der Wertvergleichsteil ist. Genau aus diesem Grund müssen Sie nicht angeben, welche Variable angewendet werden soll (außer in der Funktion mysqli_stmt_bind_param()). Hier müssen Sie auch seinen Typ angeben – in diesem Fall steht „s“ für String. Andere mögliche Typen sind: „I“ für eine Ganzzahl, „d“ für ein Double (oder Float) und „b“ für eine Binärzeichenfolge.
Die Funktionen mysqli_stmt_execute(), mysqli_stmt_bind_result() und mysqli_stmt_fetch() sind für die Ausführung von Abfragen und das Abrufen von Ergebnissen verantwortlich. Wenn Suchergebnisse vorhanden sind, werden diese angezeigt. Wenn keine Ergebnisse vorhanden sind, wird eine harmlose Meldung angezeigt. Schließlich müssen Sie die $stmt-Ressource und die Datenbankverbindung schließen und sie aus dem Speicher befreien.
Angenommen, ein legitimer Benutzer gibt die Zeichenfolge „lemming“ ein, dann gibt diese Routine (unter der Annahme der entsprechenden Daten in der Datenbank) die Meldung „Ein Lemming hat eine sehr geringe Intelligenz“ aus. Angenommen, es liegt eine vorläufige Injektion vor – zum Beispiel „lemming“ oder 1=1;“, dann gibt diese Routine die (harmlose) Meldung „Leider wurden keine Datensätze gefunden.“ aus.
Darüber hinaus bietet die MySQLi-Erweiterung auch eine objektorientierte Version derselben Routine. Im Folgenden möchten wir die Anwendung dieser Version erläutern.
<?php
$animalName = $_POST['animalName'];
$mysqli = new mysqli( 'localhost', 'username', 'password', 'database');
if ( !$mysqli ) exit( 'Verbindung fehlgeschlagen: ' . mysqli_connect_error() );
$stmt = $mysqli->prepare( 'SELECT Intelligence
if ( $stmt ) {
$stmt->bind_param( 's', $animalName );
$stmt->execute();
$stmt->bind_result( $intelligence );
if ( $stmt->fetch() ) {
print 'Ein $animalName hat $intelligence Intelligence.n';
} else {
print 'Leider wurden keine Datensätze gefunden . '; Replikation von Code – Es wird ein objektorientierter Syntax- und Organisationsansatz anstelle eines rein prozeduralen Codes angewendet.
4. Abstraktion auf höherer Ebene
Wenn Sie die externe Bibliothek PearDB verwenden, können Sie das Sicherheitsmodul der Anwendung vollständig abstrahieren.
Andererseits gibt es einen großen Nachteil bei der Verwendung dieser Bibliothek: Sie können nur durch die Ideen einiger Leute eingeschränkt werden, und die Codeverwaltung erfordert viel Arbeit. Aus diesem Grund müssen Sie sorgfältig überlegen, ob Sie sie anwenden möchten. Wenn Sie sich dafür entscheiden, stellen Sie zumindest sicher, dass sie Ihnen tatsächlich dabei helfen, die Eingaben Ihrer Benutzer zu „bereinigen“.
5. Testen Sie Ihre Injektionsschutzfunktionen
Wie wir bereits besprochen haben, besteht ein wichtiger Teil der Gewährleistung der Sicherheit Ihrer Skripte darin, sie zu testen. Zu diesem Zweck besteht der beste Ansatz darin, selbst SQL-Code-Injection-Tests zu erstellen.
Hier stellen wir ein Beispiel für einen solchen Test vor. In diesem Beispiel testen wir einen Injektionsangriff auf eine SELECT-Anweisung.
<?php
//Getestete Schutzfunktion
function safe( $string ) {
return ''' . '''
} >//Mit der Datenbank verbinden
//////////////////////
//Injektionsversuch
//// // /////////////////
$exploit = 'lemming' AND 1=1;';
//Liquidate
$safe = safe( $ Exploit );
$query = 'SELECT * FROM Animals WHERE name = $safe';
$result = mysql_query( $query );
//Testen, ob der Schutz ausreichend ist
if ( $ result && mysql_num_rows( $result ) == 1 ) {
exit 'Schutz erfolgreich:n
Exploit $exploit wurde neutralisiert.';
}
else {
exit( 'Schutz fehlgeschlagen: n
exploit $exploit konnte alle Zeilen abrufen.' );
}
?>
Wenn Sie einen solchen Testsatz erstellen und mit verschiedenen SQL-Befehlen basierend auf verschiedenen Injektionen experimentieren möchten, So erkennen Sie schnell etwaige Lücken in Ihrer Schutzstrategie. Sobald Sie diese Header korrigiert haben, können Sie sicher sein, dass Sie einen echten Schutz gegen Injektionsangriffe eingerichtet haben.
6. Zusammenfassung
Zu Beginn dieser Artikelserie haben wir eine konkrete Bedrohung Ihrer Skripte durch eine SQL-Injection-Diskussion analysiert – verursacht durch unangemessene Benutzereingaben. Anschließend haben wir die Funktionsweise der SQL-Injection beschrieben und genau analysiert, wie PHP einfach injiziert werden kann. Anschließend stellen wir ein Injektionsbeispiel aus der Praxis vor. Anschließend empfehlen wir eine Reihe von Maßnahmen, um einen versuchten Injektionsangriff unschädlich zu machen. Dies würde darin bestehen, sicherzustellen, dass alle übermittelten Werte in Anführungszeichen gesetzt werden, die Art der vom Benutzer übermittelten Werte zu überprüfen und Ihre Benutzereingaben herauszufiltern wird durch fortschrittliche Methoden wie das Überfallen gefährlicher Charaktere erreicht. Abschließend empfehlen wir Ihnen, Ihre Validierungsroutinen zu abstrahieren und Beispielskripte für die Änderung einer bestehenden Anwendung bereitzustellen. Anschließend diskutierten wir die Vor- und Nachteile von Abstraktionslösungen von Drittanbietern.