Paul: Upload: Zip Datei erkennen

Hallo zusammen,

ich möchte gern ein Upload Skript erstellen, das nur ZIP Dateien verarbeitet. Ich verwende folgendes Skript, was aber seinen Dienst offensichtlich verweigert:

[...]
$tempname = $_FILES['file']['tmp_name'];
$name .= $_FILES['file']['name'];

$type = $_FILES['file']['type'];

if ($type != "application/zip")
   {
   $error[] = "Nur zip Dateien dürfen hochgeladen werden.<br>";
   }
[...]

Ich bekomme die Fehlermeldung ausgegeben, obwohl ich eine ZIP Datei hochlade. Hat von Euch einer schonmal so eine Abfrage gemacht und weiß, wie ich das richtig überprüfen kann? Hab schon ewig nach dieser Lösung gesucht und komm einfach nicht weiter...
Danke im voraus!

Gruß, Paul

  1. Hi,

    $type = $_FILES['file']['type'];
    if ($type != "application/zip")

    hast Du $type schonmal mit echo $type; ausgegeben? Vielleicht steht dort auch "application/x-zip" drin. Außerdem ist der Typ der in $_FILES steht nicht zuverlässig. Soweit ich weis, steht dort der Typ, den der BROWSER an den Server übermittelt. Ich benutze, um auf Nummer Sicher zu gehen, immer das Unix-Kommando "file" um den Typ zuverlässig zu ermitteln:

    file -bi /home/speedesign.de/zipdatei.zip 2> /dev/null

    application/x-zip

    Das setzt natürlich voraus, dass das file-Kommando verfügbar ist und der Webserver Systemaufrufe mit system() bzw. exec() zulässt.

    viele Grüße
      Achim Schrepfer

    --
    http://reskit.speedesign.de/ - PHP-Bibliothek zum automatischen Erzeugen von HTML-Tabellen, -Formularen und -Baummenüs anhand von MySQL-Tabellen
    Selfcode: sh:) fo:| ch:| rl:° br:> n4:{ ie:% mo:} va:| de:< zu:| fl:( ss:) ls:& js:|
    1. Ich benutze, um auf Nummer Sicher zu gehen, immer das Unix-Kommando "file" um den Typ zuverlässig zu ermitteln:

      file -bi /home/speedesign.de/zipdatei.zip 2> /dev/null

      mime_content_type() ist Dir nicht kompliziert genug? ;)

      1. Hi,

        um ehrlich zu sein, von der Existenz dieser Funktion wusste ich bisher noch nichts. Aber es gibt sie ja auch erst seit PHP 4.3.0 und da ich mit Debian stable arbeite, bin ich noch bei PHP 4.1.x für Produktivsysteme.

        file -bi /home/speedesign.de/zipdatei.zip 2> /dev/null

        mime_content_type() ist Dir nicht kompliziert genug? ;)

        Schau mal, was ich in der PHP-Doku noch gefunden hab:

        <?
        if (!function_exists ("mime_content_type")) {
         function mime_content_type ($file) {
          return exec ("file -bi " . escapeshellcmd($file));
         }
        }
        ?>

        viele Grüße
          Achim Schrepfer

        --
        http://reskit.speedesign.de/ - PHP-Bibliothek zum automatischen Erzeugen von HTML-Tabellen, -Formularen und -Baummenüs anhand von MySQL-Tabellen
        Selfcode: sh:) fo:| ch:| rl:° br:> n4:{ ie:% mo:} va:| de:< zu:| fl:( ss:) ls:& js:|
    2. Hi Ihr beiden,

      danke an die Müllhalde, jetzt seh ich klarer:

      array(1) {
        ["file"]=>
        array(5) {
          ["name"]=>
          string(8) "test.zip"
          ["type"]=>
          string(28) "application/x-zip-compressed"
          ["tmp_name"]=>
          string(33) "/pfadangabe/phptmp/phpFcUU4k"
          ["error"]=>
          int(0)
          ["size"]=>
          int(30393)
        }
      }

      Trotzdem würde ich gern wissen, ob es eine Möglichkeit gibt, ganz sicher zu sein, ob es sich um eine ZIP Datei handelt. Achim, Du meintest, Du benutzt einen Systemaufruf. Ich bin leider kein UNIX Gott. Kann ich den so übernehmen, wie er da steht?

      Danke schonmal!
      Gruß, Paul.

      1. Hallo Paul,

        Trotzdem würde ich gern wissen, ob es eine Möglichkeit gibt, ganz sicher zu sein, ob es sich um eine ZIP Datei handelt. Achim, Du meintest, Du benutzt einen Systemaufruf. Ich bin leider kein UNIX Gott. Kann ich den so übernehmen, wie er da steht?

        So direkt nicht, aber in der PHP-Doku steht ein sauber ausgeführtes Beispiel, welches entweder die interne PHP-Funktion nutzt (ab PHP 4.3.0) oder eben das file-Kommando. Im Beispiel muss $file den vollständigen Dateinamen enthalten - ausgegeben wird der MIME-Typ:

        <?
        if (!function_exists ("mime_content_type")) {
         function mime_content_type ($file) {
           return chop(exec ("file -bi " . escapeshellcmd($file)));
         }
        }

        echo mime_content_type($file);
        ?>

        Ich habe noch ein chop()-Kommando eingefügt, welches evtl. auftretende Zeilenumbrüche am Ende der Ausgabe entfernt, sodass die Funktion wirklich nur den reinen MIME-Typ zurück gibt.

        viele Grüße
          Achim Schrepfer

        --
        http://reskit.speedesign.de/ - PHP-Bibliothek zum automatischen Erzeugen von HTML-Tabellen, -Formularen und -Baummenüs anhand von MySQL-Tabellen
        Selfcode: sh:) fo:| ch:| rl:° br:> n4:{ ie:% mo:} va:| de:< zu:| fl:( ss:) ls:& js:|
        1. Hallo,

          Trotzdem würde ich gern wissen, ob es eine Möglichkeit gibt, ganz sicher zu sein, ob es sich um eine ZIP Datei handelt. (...)
          So direkt nicht, aber in der PHP-Doku steht ein sauber ausgeführtes Beispiel, welches entweder die interne PHP-Funktion nutzt (ab PHP 4.3.0) oder eben das file-Kommando. (...)

          Ich würde darüber hinaus mit der Zip-Extension von PHP http://de3.php.net/manual/en/ref.zip.php arbeiten, um in Erfahrung zu bringen, ob es sich um eine fehlerfreie Zip-Datei handelt, die auch Inhalt hat.
          Ich nehme mal an, dass file nur die ersten vier Bytes prüft. Was eine solche Prüfung angeht, so ist sowohl mime_content_type() als auch der Shellaufruf von file überdimensioniert. Dasselbe ließe sich auch so bewerkstelligen:

          function is_zip ($filename) {
           if (!file_exists($filename)) return NULL;
           $zipfile=fopen($filename, 'r');
           if (!$zipfile) return NULL;
           $str=fread($zipfile, 4);
           fclose($zipfile);
           if ($str=="PK\x3\x4")
            return true;
           else
            return false;
          }

          $dateiname='bla.zip';
          $iszip=is_zip($dateiname);
          if ($iszip===true) {
           echo($dateiname.' ist eine Zip-Datei.');
           /* ... */
          } elseif ($iszip===false) {
           echo($dateiname.' ist keine Zip-Datei.');
           /* ... */
          } elseif ($iszip==NULL) {
           echo('Fehler beim Öffnen der Datei '.$dateiname.'.');
           /* ... */
          }

          Nur hat eben die Bedingung aus der magic-DAtei, dass die ersten vier Bytes PK[ETX][EOT] lauten, nichts damit zu tun, ob es eine sinnvolle Zip-Datei ist. Ich nehme einmal an, dass mime_content_type() und file eine Datei mit dem Inhalt PKbla als ZIP-Datei klassifizieren.

          Mathias

          1. Servus,

            Nur hat eben die Bedingung aus der magic-DAtei, dass die ersten vier Bytes PK[ETX][EOT] lauten, nichts damit zu tun, ob es eine sinnvolle Zip-Datei ist. Ich nehme einmal an, dass mime_content_type() und file eine Datei mit dem Inhalt PKbla als ZIP-Datei klassifizieren.

            jo, da hast Du wohl schon recht. File prüft natürlich nur die Bedingungen in der Magic-Datei. Ob die Datei sinnvoll ist, kann man so sicherlich nicht erkennen.

            Will man sicher gehen, dass eine Datei auch einen sinnvollen Inhalt hat, muss man sie stellenweise sehr aufwändig prüfen. Bei einer ZIP-Datei kann man z.B. das Archiv verifizieren lassen, bei einer JPEG-Datei könnte man versuchen die Bildgröße zu ermitteln. Um den Webserver zu schützen muss man auch dafür sorgen, dass die Dateiendung stimmt bzw. im Upload-Verzeichnis keine Skripte ausgeführt werden.

            viele Grüße
              Achim Schrepfer

            --
            http://reskit.speedesign.de/ - PHP-Bibliothek zum automatischen Erzeugen von HTML-Tabellen, -Formularen und -Baummenüs anhand von MySQL-Tabellen
            Selfcode: sh:) fo:| ch:| rl:° br:> n4:{ ie:% mo:} va:| de:< zu:| fl:( ss:) ls:& js:|
        2. Hallo Achim,

          <?
          if (!function_exists ("mime_content_type")) {
          function mime_content_type ($file) {
             return chop(exec ("file -bi " . escapeshellcmd($file)));
          }
          }

          echo mime_content_type($file);
          ?>

          ich habe mir erlaubt, Dein Skript etwas umzuschreiben, da diese Version bei mir nichts zurückgibt. Folgende Funktion liefert mir jetzt als Ausgabe "127". Hmmm, kann ich mit diesem Wert was anfangen?

          if (!function_exists ("mime_content_type")) {
           function mime_content_type ($file) {
             exec ("file -bi " , escapeshellcmd($file), $ergebnis);
             chop($ergebnis);
             return $ergebnis;
           }
          }

          Asö, könnte natürlich auch sein, daß in Deiner Abfrage was anderes abfragst, aber das weiß ich nicht, kann Dein Kommando aufgrund mangelnder Erfahrung ja nicht verstehen... Ich weiß, ich sollte mehr UNIX machen... ;-)

          viele Grüße,
          Paul

          1. Hallo Paul,

            ich habe mir erlaubt, Dein Skript etwas umzuschreiben, da diese Version bei mir nichts zurückgibt.

            hast du mal versucht exec mit zwei Parametern aufzurufen und zu schauen was dann im 2. Parameter (einem Array) drinsteht?

            Folgende Funktion liefert mir jetzt als Ausgabe "127". Hmmm, kann ich mit diesem Wert was anfangen?

            was du damit anfangen kannst, weiß ich nicht, aber diese 127 ist der Rückgabestatus des ausgeführen Befehls (siehe http://de.php.net/function.exec) - was 127 aber bedeutet, weiß ich auch nicht :-)

            function mime_content_type ($file) {
               exec ("file -bi " , escapeshellcmd($file), $ergebnis);

            das Komma vor escapeshellcmd muss ein Punkt sein - du willst die Strings ("file -bi " und die Rückgabe von escapeshellcmd) doch verbinden und nicht escapeshellcmd der Funktion exec als zweiten Parameter übergeben. Außerdem sollstest du die Rückgabe von exec speichern und die zurückgeben.

            Grüße aus Nürnberg
            Tobias

            --
            Selfcode: sh:( fo:) ch:? rl:( br:< n4:& ie:% mo:| va:) de:] zu:) fl:( ss:| ls:[ js:|
            1. Servus,

              hier mal ein Zitat aus der exec()-Doku:
              "exec() führt ein gegebenen Befehl aus, ohne eine Ausgabe zu erzeugen. Die Funktion gibt lediglich die letzte Zeile aus dem Befehlsergebnis zurück."

              Paul, als erstes solltest Du mal ein bisschen debuggen: lass mal folgendes Skript laufen:
              <?=exec('file -bi eineexistierendedatei.ext')?>

              Wenn Du das Skript im Browser aufrufst, müsste etwas wie "text/plain; charset=iso-8859-1" zu lesen sein. Wenn nicht, ist wohl das file-Kommando auf dem Server nicht verfügbar.

              Probier erst mal das aus und schreibe, ob es geklappt hat.

              viele Grüße
                Achim Schrepfer

              --
              http://reskit.speedesign.de/ - PHP-Bibliothek zum automatischen Erzeugen von HTML-Tabellen, -Formularen und -Baummenüs anhand von MySQL-Tabellen
              Selfcode: sh:) fo:| ch:| rl:° br:> n4:{ ie:% mo:} va:| de:< zu:| fl:( ss:) ls:& js:|
  2. ich möchte gern ein Upload Skript erstellen, das nur ZIP Dateien verarbeitet. Ich verwende folgendes Skript, was aber seinen Dienst offensichtlich verweigert:

    if ($_FILES['file']['type'] != "application/zip")
       {
       $error[] = "Nur zip Dateien dürfen hochgeladen werden.<br>";
       }

    Ich bekomme die Fehlermeldung ausgegeben, obwohl ich eine ZIP Datei hochlade.

    Hilf Dir selbst, dann hilft Dir Gott. Falls eine if-Abfrage oder ähnliches partout nicht das gewünschte Ergebnis liefert, sollte man als erstes prüfen, ob die beteiligten Variablen überhaupt das enthalten, von dem man meint, daß sie es enthalten müssten:

    echo "<pre>"; var_dump($_FILES); echo "</pre>";

    Ausgabe:

    array(1) {
      ["file"]=>
      array(5) {
        ["name"]=>
        string(12) "zippel.zip"
        ["type"]=>
        string(28) "application/x-zip-compressed"
        ["tmp_name"]=>
        string(14) "/tmp/phpxyz"
        ["error"]=>
        int(0)
        ["size"]=>
        int(123)
      }
    }

    Und jetzt bist Du dran.

  3. Hello,

    $type = $_FILES['file']['type'];

    Trau niemals einem Client!

    Wenn Deine PHP-Version < 4.3.0 ist, dann schau Dir mal das Linux-Programm file an. Das kann man auch über system() aufrufen, sofern vorhanden und für den User (Webserver) zugänglich ( safe_mode(), safe_dir )

    Liebe Grüße aus http://www.braunschweig.de

    Tom

    --
    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen