Christian Seiler: SetEnfIf auf SESSION anwendbar?

Beitrag lesen

Hallo,

gebe ich nun die domain + /pics/bild1.jpeg in meinen browser ein (ohne session id, etc) dann wird im normalfall dieses bild auch angezeigt. das möchte ich vermeiden, da es nach meinem geschmack eine lücke ist

Du scheinst nicht zu verstehen, was die Leute Dir hier im Thread sagen wollen. ;-)

Natürlich ist das eine Lücke, wenn man das Bild direkt erreichen kann. Deswegen wurde Dir hier vorgeschlagen, ein PHP-Script zu schreiben, das die Bilder nach einer Überprüfung ausliefert. Dieses kann dann auch in der Session überprüfen, ob der Zugriff erlaubt ist. Im Prinzip sähe das dann so aus:

  1. /pics komplett für den HTTP-Zugriff sperren (Order deny, allow sowie Deny from all) - dann kann *niemand* mehr von außen auf die Bilder zugreifen.

  2. Bilder so in den HTML-Code einbinden:

<img src="/pics.php/bild1.jpeg" ...>

Dann wird also content.php aufgerufen und $_SERVER['PATH_INFO'] auf '/bild1.jpeg' gesetzt.

Der Witz ist nun, dass sich das PHP-Script 'pics.php' darum kümmern muss, die Bilder an den Browser zu schicken, d.h. bei jedem Zugriff auf ein Bild wird das PHP-Script selbst ausgeführt und kann den Zugriff überprüfen.

  1. Das Script, das die Dateien ausliefert, sieht dann in etwa so aus:
<?php  
  
// =================================================================  
// Konfiguration  
// =================================================================  
// Alle Pfade sind relativ zum Verzeichnis in dem sich content.php  
// befindet. Das '/pics' angefügt ist WICHTIG, damit man mit diesem  
// Script nicht Zugriff auf *alle* Rohdaten hat.  
$basePath = realpath (dirname (__FILE__).'/pics');  
  
// Erlaubte Dateiendungen  
$allowedExtensions = array ('.jpeg', '.jpg', '.png', '.gif', '.pdf');  
  
// MIME-Typ-Mappings  
$mimeMappings = array (  
  '.jpeg' => 'image/jpeg',  
  '.jpg' => 'image/jpeg',  
  '.png' => 'image/png',  
  '.gif' => 'image/gif',  
  '.pdf' => 'application/pdf'  
);  
// =================================================================  
  
// 1. Schritt: Sitzung starten und überprüfen, ob Zugriff mit der aktuellen  
// Sitzung überhaupt zulässig ist  
session_start ();  
  
if (!isset ($_SESSION['access'])) {  
  Header ('HTTP/1.1 403 Forbidden');  
  exit;  
}  
  
// 2. Schritt: $_SERVER['PATH_INFO'] holen und damit den Pfad des  
// auszuliefernden Bildes  
// $path enthält dann sowas wie '/..../pics/bild1.jpeg'  
$path = $basePath.$_SERVER['PATH_INFO'];  
  
// 3. Schritt: Sicherheitsüberprüfung: realpath() nutzen und dann  
// prüfen, ob das Ergebnis wieder mit dem gleichen Basispfad anfängt  
$path = realpath ($path);  
if ($path === false || substr ($path, 0, strlen ($basePath)) != $basePath) {  
  Header ('HTTP/1.1 404 Not found');  
  exit;  
}  
  
// 4. Schritt: Weitere Überprüfung: Ist das eine Datei, die wir ausliefern  
// wollen?  
if (!is_file ($path)) {  
  Header ('HTTP/1.1 404 Not found');  
  exit;  
}  
  
// 5. Schritt: Dateiendung überprüfen  
$file = basename ($path);  
$pos = strrpos ($file, '.');  
if ($pos === false) {  
  // Keine Dateiendung  
  Header ('HTTP/1.1 404 Not found');  
  exit;  
}  
$extension = substr ($file, $pos);  
if (!in_array ($extension, $allowedExtensions)) {  
  // Dateiendung nicht zulässig  
  Header ('HTTP/1.1 404 Not found');  
  exit;  
}  
  
// 6. Schritt: Korrekten MIME-Type senden  
$mimeType = $mimeMappings[$extension];  
Header ("Content-Type: $mimeType");  
  
// 7. Schritt: Die Datei selbst senden  
readfile ($path);  
  
?>

Bitte beachte, dass das nur die Ausgangsbasis für eine mögliche Implementierung darstellt und dass man das ganze auch gut erweitern kann. Allerdings habe ich hier schonmal aufgeführt, was mindestens an Sicherheitschecks in so ein Script hinein müsste. Mögliche Angriffe auf so ein Script wären (damit Dir klar ist, warum die Checks drin sind):

* Auslieferung einer beliebigen Datei im gesamten Dateisystem, indem
   man z.B. http://irgendwas/pics.php/../../../../datei aufruft. Dies
   wird durch die realpath()-Abfrage unterbunden: realpath() löst immer
   den gesamten Dateinamen auf, d.h. wenn man zum Beispiel als Angreifer
   http://irgendwas/pics.php/../../../../../../../../../etc/passwd aufruft,
   um /etc/passwd lesen zu können, dann steht nach dem realpath()-Aufruf
   in $path eben '/etc/passwd' direkt drin. Und dann kann man mit dem
   Vergleich, ob $path mit $basePath anfängt, abfragen, ob der Pfad denn
   wirklich noch in dem erlaubten Pfad enthalten ist oder nicht.

[realpath() wird auf den Inhalt von $basePath übrigens nur deshalb
   angewendet, damit man das ganze hinterher auch brauchbar vergleichen
   kann als String. Es gibt Situationen, in denen der Vergleich, dass
   $path mit $basePath anfängt immer fehlschlagen würde, wenn man
   realpath() ganz am Anfang nicht schon auf den Inhalt von $basePath
   anwendet.]

* Auslieferung des Quellcodes einer PHP-Datei, zum Beispiel der Datei,
   in der die Datenbankzugriffsdaten drin stehen. Dies wird durch zwei Dinge
   unterbunden:

1) $basePath wird auf das '/pics'-Verzeichnis direkt gesetzt und nicht
      auf das gesamte Webroot.

2) Die Dateiendungen werden gegen eine Liste von erlaubten Dateiendungen
      gematcht. Damit werden nur Dateien ausgeliefert, von denen man sich
      sicher ist, dass die Auslieferung der Datei selbst keine
      Sicherheitsproblematik darstellt.

* Zugriff einer fremden Person auf die Dateien. Dies wird von vorne herein
   damit unterbunden, dass als allererstes der Zugriff auf die Session
   geprüft wird.

Wenn Du nun die Situation hast, dass nicht alle Dateien in /pics beschränkt  sein sollen, würde ich Dir dennoch empfehlen, das einfach in zwei verschiedene Verzeichnisse aufzuteilen: Eines, das für alle zugänglich ist über normales HTTP und eines das über normales HTTP gesperrt ist und wofür man das Script braucht. Das ist die sauberste Lösung für dieses Problem.

Ansonsten: Bitte versuche, das Script durchzugehen und Zeile für Zeile zu verstehen, was es da macht und warum es das so macht. Bitte versuche erst dann, am Script herumzubauen, oder Du läufst Gefahr, Dir eine riesige Sicherheitslücke aufzureißen.

Oh, achja, das Script ist aus dem Kopf und nicht getestet, eventuelle Syntaxfehler durch vergessene ; bitte ich zu verzeihen.

Viele Grüße,
Christian