Jochen: file_get_contents und Sicherheit

Hallo,

ich möchte für eine öffentliche Webseite einen XML Parser für bestimmte XML-Services anbieten, die auf X-beliebigen Servern angeboten werden können.
Das ganze habe ich bereits für den Admin-Bereich erfolgreich realisiert.
Dabei geht es darum bestimmte WebServices zu validieren und auszulesen und nach Wunsch mit  den Ergebnissen einer Clientanwendung via Javascript temporär Objekte hinzuzufügen.
Dabei übergebe ich via Ajax die eingegebene URL und verarbeite das Ergebnis mit PHP:

  
$cap_url = strtolower($_POST["capUrl"]);  
  
if (false !== @file_get_contents($cap_url)){  
  
	$wms_file = file_get_contents($cap_url);  
	  
        //Simplexml-Objekt  
	$XML = @simplexml_load_string($cap_file);  
	  
	if (isset($XML->Service)) {  
		echo '<strong>Service:</strong><br>';  
        }  
... ect.  
  

Jetzt frage ich mich ob ich sowas aus sicherheitstechnischer Sicht anbieten kann, oder ob ich einen anderen Weg beschreiten muss?

Danke für jeden Tipp!
Jo

  1. Tach!

    Jetzt frage ich mich ob ich sowas aus sicherheitstechnischer Sicht anbieten kann, oder ob ich einen anderen Weg beschreiten muss?

    Sicherheit ist kein fest definierter Begriff. Was genau möchtest du denn beim Laden der Ressource absichern?

    if (false !== @file_get_contents($cap_url)){

    $wms_file = file_get_contents($cap_url);

    Warum holst du denn das Zeug zweimal? Ein

    if (($wms_file = @file_get_contents($cap_url)) !== false){

    tut es auch. Und vermutlich willst du auch keine Dateiinhalte bestehend aus Leerstring oder 0 verarbeiten, also kann der typsichere Vergleich auch zu einem einfachen umgeschrieben werden.

    // both empty string and string containing only a 0 are also not valid
    if ($wms_file = @file_get_contents($cap_url)) {

    Es sei denn, du möchtest Übertragungsfehler und inhaltliche Fehler voneinander getrennt behandeln, dann brauchst du die typsichere Variante.

    dedlfix.

  2. $cap_url = strtolower($_POST["capUrl"]); //[1]
    //[3]
    if (false !== @file_get_contents($cap_url)){ //[2]

    $wms_file = file_get_contents($cap_url); //[2]
         /* ... */

    
    > Jetzt frage ich mich ob ich sowas aus sicherheitstechnischer Sicht anbieten kann, oder ob ich einen anderen Weg beschreiten muss?  
    
    [1] das strtolower könnte teils zu Fehlern führen, wenn zB. case-sensitive Daten als Parameter zur URL gehören.  
    [2] Zweimal laden? Klingt doof. `if(($wms_file = @file_get_contens($cap_url)) !== false)`{:.language-php}  
    [3] Zusätzliche Kontrollen, ob es wirklich eine eine entfernte Seite oder eine lokale Datei ist sollten mindestens noch mit drin sein.  
      
    Allgemein würde ich wohl erstmal mit HEAD den Content-Type ermitteln, wenn der nicht XML ist kannst du entsprechende Meldung bringen. Zusätzlich hält es deinen Traffic klein. (Was wäre denn z.B. wenn ein Bösewicht massenhaft Requests schickt, bei denen dein Server auf einmal gigabyte-große Dateien runterladen soll?)  
      
    Was genau alles beachtet werden muss kann ich dir aber auch nicht so genau sagen, zu mal es da wahrscheinlich auch noch gesetzliche Hürden/Probleme geben könnte. (z.B. jemand der dir/deinem Service schaden will, lässt deinen Server illegale Inhalte herunterladen, Kinder-Pornos und dergleichen)  
      
    MfG  
    bubble
    
    -- 
    If "god" had intended us to drink beer, he would have given us stomachs. - David Daye
    
  3. $cap_url = strtolower($_POST["capUrl"]);

    Das lässt scheinbar Zugriff auf jedes und alles zu, was irgendwie lesbar ist. Du wirst weitere Prüfungen machen müssen.

    Zusätzlich sehe ich die Gefahr, dass Dein Service für eine DDoS-Attacke missbraucht werden kann. Es genügen wenige Bytes Anfrage um eine größere Aktion auf Deinem Server zu veranlassen, ggf. eine noch größere auf den zu attackierenden Hosts. Du wirst auch dazu weitere Prüfungen machen müssen.

    
    > if (false !== @file_get_contents($cap_url)){  
    > 	$wms_file = file_get_contents($cap_url);
    
    

    Hm. Du probierst die Datei komplett zu lesen um sie dann nochmals komplett lesen.

    $cap_file = file_get_contents($cap_url)  
    if (false == $cap_file) {  
    # Das reicht, weil auch wenn Die Datei leer war, null oder '0' enthält nichts zu tun ist  
        # Fehlermeldung etc.  
        # Abbruch  
    }  
      
    # weiter mit der Verarbeitung
    

    Was die Idee generell betriff. Ok. ok. Nur musst Du Dir im Klaren sein, was passiert, wenn sich eines Tages herausstellt, dass die verschwendeten Funktionen irgendwelche Sicherheitsprobleme machen und vielleicht sogar dazu führen, dass die Ersteller der vermeintlichen xml-Dateien auf Deinem System eigenen Code ausführen können.

    Jörg Reinholz

  4. Hallo,
    Ich stand kürzlich für ein Hobby-Projekt auch vor einem ähnlichen Problem (bei mir ging es um HTML statt XML, aber die Grundproblematik ist die gleiche).
    Was mir noch einfiele, was Du noch machen könntest (ohne Anspruch auf Vollständigkeit zu erheben):

    1.) Prüfe vor dem Herunterladen, ob die URL gültig ist und sich nicht um lokale Pfade o.ä. handelt, das kannst du z.b. mit parse_url() bewerkstelligen.
    So sieht das bei mir aus:

      
    $parseResult = parse_url($this->url);  
    if ((($parseResult["scheme"] != "http") && ($parseResult["scheme"] != "https")) || (!isset($parseResult["host"]))) {  
    ... (keine gueltige URL)...  
    } else {...}  
    
    

    Je nach Konfiguration deines Webservers solltest Du ggf. sicherstellen, dass die URL nicht auf dem lokalen server liegt (oft sind ja Webserver so konfiguriert, dass sie bestimmte HTTP-Dienste ohne Authentifizierung anbieten, wenn der Aufruf vom Localhost kommt, das könnten sich Angreifer zu Nutze machen)

    2.) Gib file_get_contents eine Maximal-Länge mit. Somit vermeidest Du, dass man mit zu großen Datenmengen deinen Service lahmlegen kann.

    3.) Prüfe die heruntergeladenen Daten auf den korrekten File-Typ. Ich mache das bei mir durch Untersuchung des Mimetypes (was nicht 100%tig zuverlässig ist, aber zumindest verhindert, dass das S<kript jeden Mist akzeptiert):

      
    ...  
    // Fetch HTML page  
    $html = file_get_contents ($this->url, false, null, -1 , $this->maxSize);  
    // Check mimetype  
    $finfo = finfo_open(FILEINFO_MIME);  
    if (!preg_match ("/^text\/html/", finfo_buffer($finfo, $html))) {  
    ... (herunter geladene Datei ist kein HTML)...  
    }  
    ...  
    
    

    Viele Grüße,
    Jörg