PHP 8.4.2 Released!

Sicherheit des Dateisystems

Inhaltsverzeichnis

PHP ist hinsichtlich der Berechtigungen auf Datei- und Verzeichnisebene von den in den meisten Serversystemen implementierten Sicherheitseinstellungen abhängig. Dies verleiht Ihnen Kontrolle darüber, welche Dateien im Dateisystem gelesen werden dürfen. Es sollte sichergestellt werden, dass alle öffentlich zugänglichen Dateien von allen Benutzern, die Zugang zu diesem Dateisystem haben, gelesen werden können, ohne die Sicherheit zu gefährden.

Da PHP entwickelt wurde, um Zugriffe auf das Dateisystem auf Benutzebene zu erlauben, ist es natürlich auch möglich, ein PHP-Skript zu schreiben, dass Ihnen erlaubt, Systemdateien wie /etc/passwd zu lesen, Ethernetverbindungen zu modifizieren, enorme Druckaufträge zu senden etc. Dies hat offensichtliche Implikationen, indem Sie sicherstellen müssen, dass alle von Ihnen zu lesenden bzw. zu schreibenden Dateien auch die richtigen sind.

Stellen Sie sich folgendes Skript vor, in dem ein Benutzer zum Ausdruck bringt, dass er eine Datei in seinem Heimatverzeichnis löschen möchte. Es wird davon ausgegangen, dass ein PHP-Webinterface regelmäßig für das Dateimanagement verwendet wird und dass der Apache-Benutzer berechtigt ist, in seinem Heimatverzeichnis Dateien zu löschen.

Beispiel #1 Schlechte Variablenprüfung führt zu....

<?php

// Löschen einer Datei aus dem Heimatverzeichnis des Users
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";

unlink("$homedir/$userfile");

echo
"Die Datei wurde gelöscht!";

?>
Da Benutzer- und Dateiname über ein Benutzerformular bereitgestellt werden, kann jeder jemand anderes Benutzer- und Dateinamen übertragen und so Dateien löschen, ohne über die entsprechnde Erlaubnis zu verfügen. In diesem Fall empfiehlt es sich, eine andere Form der Authentifizierung zu verwenden. Stellen Sie sich vor was passieren würde, wenn die übertragenen Variablen "../etc/" und "passwd" beinhalten würden. Der Code würde dann folgendermaßen aussehen:

Beispiel #2 ... Ein Angriff auf das Dateisystem

<?php

// Löscht eine Datei irgendwo auf der Festplatte, wo der Benutzer
// die nötigen Rechte besitzt. Wenn PHP root-Zugriff hat:
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"

unlink("$homedir/$userfile"); // "/home/../etc/passwd"

echo "Die Datei wurde gelöscht!";?>

?>
Es gibt zwei wichtige Kriterien die Sie beachten sollten, um diese Dinge zu vermeiden:
  • Erteilen Sie dem PHP Web-user (Binärdatei) nur eingeschränkte Rechte.
  • Prüfen Sie alle übertragenen Variablen.
Hier ist ein verbessertes Skript:

Beispiel #3 Etwas sicherere Prüfung des Dateinamens

<?php

// Löscht eine Datei von der Festplatte, auf die
// der PHP-Benutzer Zugriff hat.
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanism
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";

$filepath = "$homedir/$userfile";

if (
file_exists($filepath) && unlink($filepath)) {
$logstring = "$filepath gelöscht\n";
} else {
$logstring = "$filepath konnte nicht gelöscht\n";
}

$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $logstring);
fclose($fp);

echo
htmlentities($logstring, ENT_QUOTES);

?>
Auch dies nicht ohne Schwachstellen. Wenn Ihr Authentifizierungssystem Benutzern erlauben sollte, deren eigene Logins zu kreieren, und ein Benutzer wählt den Login "../etc/", ist das System erneut angreifbar. Aus diesem Grund sollten Sie eventuell eine speziellere Überprüfung durchführen

Beispiel #4 Sicherere Dateinamensprüfung

<?php

$username
= $_SERVER['REMOTE_USER']; // Nutzung eines Authentifikationsmechanismus
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";

$filepath = "$homedir/$userfile";

if (!
ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die(
"Ungültiger Benutzer- oder Dateiname");
}

// etc.

?>

Abhängig vom Betriebssystem gibt es eine große Anzahl Dateien, auf die Sie achten sollten, inklusive Einträge für Geräte (/dev/ oder com1), Konfigurationsdateien (die Dateien in /etc/ und die .ini-Dateien), allgemein bekannte Verzeichnisse (/home/, My Documents) etc. Aus diesem Grund ist es für gewöhnlich einfacher, eine Richtlinie zu erstellen, in der alles verboten ist, was Sie nicht ausdrücklich erlauben.

add a note

User Contributed Notes 5 notes

up
99
anonymous
19 years ago
(A) Better not to create files or folders with user-supplied names. If you do not validate enough, you can have trouble. Instead create files and folders with randomly generated names like fg3754jk3h and store the username and this file or folder name in a table named, say, user_objects. This will ensure that whatever the user may type, the command going to the shell will contain values from a specific set only and no mischief can be done.

(B) The same applies to commands executed based on an operation that the user chooses. Better not to allow any part of the user's input to go to the command that you will execute. Instead, keep a fixed set of commands and based on what the user has input, and run those only.

For example,
(A) Keep a table named, say, user_objects with values like:
username|chosen_name |actual_name|file_or_dir
--------|--------------|-----------|-----------
jdoe |trekphotos |m5fg767h67 |D
jdoe |notes.txt |nm4b6jh756 |F
tim1997 |_imp_ folder |45jkh64j56 |D

and always use the actual_name in the filesystem operations rather than the user supplied names.

(B)
<?php
$op
= $_POST['op'];//after a lot of validations
$dir = $_POST['dirname'];//after a lot of validations or maybe you can use technique (A)
switch($op){
case
"cd":
chdir($dir);
break;
case
"rd":
rmdir($dir);
break;
.....
default:
mail("webmaster@example.com", "Mischief", $_SERVER['REMOTE_ADDR']." is probably attempting an attack.");
}
up
25
fmrose at ncsu dot edu
19 years ago
All of the fixes here assume that it is necessary to allow the user to enter system sensitive information to begin with. The proper way to handle this would be to provide something like a numbered list of files to perform an unlink action on and then the chooses the matching number. There is no way for the user to specify a clever attack circumventing whatever pattern matching filename exclusion syntax that you may have.

Anytime you have a security issue, the proper behaviour is to deny all then allow specific instances, not allow all and restrict. For the simple reason that you may not think of every possible restriction.
up
22
devik at cdi dot cz
23 years ago
Well, the fact that all users run under the same UID is a big problem. Userspace security hacks (ala safe_mode) should not be substitution for proper kernel level security checks/accounting.
Good news: Apache 2 allows you to assign UIDs for different vhosts.
devik
up
10
Latchezar Tzvetkoff
15 years ago
A basic filename/directory/symlink checking may be done (and I personally do) via realpath() ...

<?php

if (isset($_GET['file'])) {
$base = '/home/polizei/public_html/'; // it seems this one is good to be realpath too.. meaning not a symlinked path..
if (strpos($file = realpath($base.$_GET['file']), $base) === 0 && is_file($file)) {
unlink($file);
} else {
die(
'blah!');
}
}
?>
up
6
cronos586(AT)caramail(DOT)com
22 years ago
when using Apache you might consider a apache_lookup_uri on the path, to discover the real path, regardless of any directory trickery.
then, look at the prefix, and compare with a list of allowed prefixes.
for example, my source.php for my website includes:
if(isset($doc)) {
$apacheres = apache_lookup_uri($doc);
$really = realpath($apacheres->filename);
if(substr($really, 0, strlen($DOCUMENT_ROOT)) == $DOCUMENT_ROOT) {
if(is_file($really)) {
show_source($really);
}
}
}
hope this helps
regards,
KAT44
To Top