Mike: PhP bilder upload + URL in sql DB

Servus!

Ich würde gerne aus einem Formular heraus einen Bilderupload zulassen. Nun kam ich auf die idee via BLOB bilder in die DB zu speichern, was aber anscheinend nicht sonderlich toll ist. Darum würde ich gerne versuchen das Bild ins filesystem zu laden und die url in die DB zu setzen. Aber ich glaube ich habe einige verständniss probleme, vllt könnt ihr mir helfen:

Beispiel Script von php.net:

  
$uploaddir = '/var/www/uploads/';  
$uploadfile = $uploaddir. basename($_FILES['userfile']['name']);  
print "<pre>";  
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {  
    print "File is valid, and was successfully uploaded.";  
    print "Here's some more debugging info:\n";  
    print_r($_FILES);  
} else {  
    print "Possible file upload attack!  Here's some debugging info:\n";  
    print_r($_FILES);  
}  
print "</pre>";  
  
?>  

$uploaddir -> wie wird diese genau definiert? Bzw wenn ich ein Ordner, am selben ort mache wie das script laufen soll, kann ich dann einfach "ordner/" eintragen?

mit dem move_uploaded_file wird das bild aus dem tmp verzeichnis in meinen ordner hinein verschoben, kann ich also vorher noch einige bedingungen setzen, zB solche ergänzungen:

if($_FILES['userfile']['size']>10000 || $_FILES['userfile']['size']==0){
  echo "ERROR";
}else{
 // Hier käme das move uploaded rein
}

Wäre sowas richtig?

Bezüglich der URL, wo bekomme ich die her? Ich will ja in eine DB eine Id den Namen und die url speichern. Aber wo bekomme ich die url eig. her?

Die Inserts in die DB kämen dementsprechend auch ins obige else oder? Denn der move_uploaded_file befehl wird ja nur ausgeführt fals keine errors auftreten.

Habe ich das in etwa richtig verstanden?

  1. Hello,

    ich sehe schon, es wird immer wichtiger, dass ich meinen Artikel im Wiki endlich mal fertig mache.

    http://wiki.selfhtml.org/wiki/Artikel:PHP/File_Upload

    Ich würde gerne aus einem Formular heraus einen Bilderupload zulassen. Nun kam ich auf die idee via BLOB bilder in die DB zu speichern, was aber anscheinend nicht sonderlich toll ist.

    Das kommt auf Dein Datenmodell an, dass du für die Bilderdatenbank wählst. Und wenn die Bilder kleiner als 100kB sind, dann ist die Datenbank durchaus ein passender Speicherplatz.

    Allerdings muss man sich dann "select * from bilder" auf jeden Fall verkneifen, sondern muss eben immer die kleinstmögliche Ergebnismenge erzeugen.

    Außerdem kann man die Bilder dann nicht direkt einbinden in einenn HTTP/s-Request. Man benötigt immer ein Script für die Auslieferung. Dieses Script muss dann im Prinzip _sehr_ komplex sein, denn es müsste auch "chunked", "compressed" und "last modified" unterstützen. Das kann jeder übliche Webserver nämlich von Haus aus.

    Wenn die Bilder im Filesystem gespeichert werden, muss man darauf achten, dass dar Request auf das Bild auf keinen Fall einen Scriptparser (z.B. PHP, Perl) anschmeißt, der das Bild als ausführbare Datei werten könnte. Damit hat man sich dann sofort eine empfindliche Sicherheitslücke gebaut.

    Darum würde ich gerne versuchen das Bild ins filesystem zu laden und die url in die DB zu setzen. Aber ich glaube ich habe einige verständniss probleme, vllt könnt ihr mir helfen:

    Gerne.

    Beispiel Script von php.net:
    [code lang=php]
    $uploaddir = '/var/www/uploads/';
    $uploadfile = $uploaddir. basename($_FILES['userfile']['name']);
    print "<pre>";
    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {

    move_uploaded_file() & Co. sind antiquiert. Bei Verwendung des (neuen) superglobalen Arrays $_FILES gibt es andere Möglichkeiten um festzustellen, ob das File hochgeladen wurde. Und diese sollten auch benutzt werden!

    Wichtig ist die Einstellung eines eigenen upload_tmp_dir, auf das kein anderer Serveruser des Shared Hosting Zugriff hat und insbesondere NIEMALS ein Client Zugriff erhält (z. B. per HTTP/s). Das würde dann auch den Garaus für den Server bedeuten können.

    Das Ablage-Verzeichnis für die Bilder sollte mit "engine off" für den Parser ausgeschaltet werden und die Endungen für die Bilder im Ablageverzeichnis sollten stimmen. Das kann man mit getimagesize() überprüfen und dann selber entsprechend richtigstellen. Ein GIF-Image sollte also auch als ???.gif abgespeichert werden, auch wenn der Client es als *.png oder als *.jpg gesesndet hat.

    Derartige Fehler (Endungen passen nicht zum MIME-Type) gibt es auch noch in vielen CMS. Bilder, die durch getimagesize() nicht erkannt werden, sollte man sowieso ablehnen.

    Je nachdem, ob PHP als Modul oder als CGI oder FastCGI läuft, muss man noch weitere Sicherheitsmaßnahmen ergreifen.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Danke für deine Antwort, bis jetzt siehts so aus:

        
      $uploaddir = 'uploaded_img/';  
      $uploadfile = $uploaddir.basename($_FILES['image']['name']);  
      if($_FILES['image']['size'] == 0 || $_FILES['image']['size'] > 100000 || !getimagefilesize($_FILES['image'])){  
      $control2 = 1;  
      $error2 = 'Fehlerhaftes Bild! Bitte Hinweise beachten!';  
      }else{  
      if(move_uploaded_file($_FILES['image']['tmp_name'], $uploadfile)){  
      $control2 = 0;  
      }  
      }  
      		  
      
      

      Zusätzlich werde ich dem verzeichnis aus dem Server nur lese rechte geben.

      Ist dass in etwa okay?

    2. Hi!

      [...] Script für die Auslieferung. Dieses Script muss dann im Prinzip _sehr_ komplex sein, denn es müsste auch "chunked", "compressed" und "last modified" unterstützen.

      Worin genau siehst du diesen Zwang begründet? Abgesehen davon, dass man HTTP 1.0 verwenden könnte, sind die genannte Dinge keine Pflichtveranstaltung.

      move_uploaded_file() & Co. sind antiquiert. Bei Verwendung des (neuen) superglobalen Arrays $_FILES gibt es andere Möglichkeiten um festzustellen, ob das File hochgeladen wurde. Und diese sollten auch benutzt werden!
      Wichtig ist die Einstellung eines eigenen upload_tmp_dir,

      Das kann man als Anwender nicht unbedingt beeinflussen (php.ini oder httpd.conf müssen änderbar sein). Man kann es zumindest über phpinfo() kontrollieren. Wenn das auf /tmp oder ein anderes gemeinsames Verzeichnis zeigt, wüsste ich grad nicht, wie einem das Ignorieren von *_uploaded_file() weiterhilft.

      Je nachdem, ob PHP als Modul oder als CGI oder FastCGI läuft, muss man noch weitere Sicherheitsmaßnahmen ergreifen.

      Die da wären? (Oder stehen die schon im Wiki aufgeführt?)

      Lo!

      1. Hello,

        [...] Script für die Auslieferung. Dieses Script muss dann im Prinzip _sehr_ komplex sein, denn es müsste auch "chunked", "compressed" und "last modified" unterstützen.

        Worin genau siehst du diesen Zwang begründet? Abgesehen davon, dass man HTTP 1.0 verwenden könnte, sind die genannte Dinge keine Pflichtveranstaltung.

        WEnn man die Vor- und Nachteile unterschiedlicher Speichertechniken vergleicht, sollte man doch auch ihre Nebenwirkungen erwähnen, oder? Und für die Vergangenheit planen und entwickeln wir doch nur noch selten (ich lebe davon von zu Zeit noch). Da sollte man dann also die 1.1-Methoden nicht unterschlagen...

        move_uploaded_file() & Co. sind antiquiert. Bei Verwendung des (neuen) superglobalen Arrays $_FILES gibt es andere Möglichkeiten um festzustellen, ob das File hochgeladen wurde. Und diese sollten auch benutzt werden!
        Wichtig ist die Einstellung eines eigenen upload_tmp_dir,

        Das kann man als Anwender nicht unbedingt beeinflussen (php.ini oder httpd.conf müssen änderbar sein). Man kann es zumindest über phpinfo() kontrollieren. Wenn das auf /tmp oder ein anderes gemeinsames Verzeichnis zeigt, wüsste ich grad nicht, wie einem das Ignorieren von *_uploaded_file() weiterhilft.

        Das haben wir neulich schon etwas ausführlicher diskutiert. *_uploaded_file() stellt überhaupt nicht fest, ob das File seit dem Upload verändert wurde, sondern nur, ob es hochgeladen wurde. Und das ist nutzlos. Das kann man auch mit $_FILES feststellen.

        Wichtig ist, dass das File am Temporärort nicht erreichbar und nicht veränderbar ist durch Dritte. Das ist einer der empfindlichsten Punkte. Dazu darf man dann aber upload_tmp_dir auch KEINESFALLS in seine Dokument Root verlegen und für HTTP/s erreichbar machen.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hi!

          WEnn man die Vor- und Nachteile unterschiedlicher Speichertechniken vergleicht, sollte man doch auch ihre Nebenwirkungen erwähnen, oder? Und für die Vergangenheit planen und entwickeln wir doch nur noch selten (ich lebe davon von zu Zeit noch). Da sollte man dann also die 1.1-Methoden nicht unterschlagen...

          Und wo ist nun der Zwang, die genannten Features zu implementieren?

          *_uploaded_file() stellt überhaupt nicht fest, ob das File seit dem Upload verändert wurde, sondern nur, ob es hochgeladen wurde. Und das ist nutzlos. Das kann man auch mit $_FILES feststellen.

          Gut, es bringt also mit $_FILES[...]['tmp_name'] keinen Vorteil, aber auch keinen Nachteil, wenn man das Upload-Directory nicht beeinflussen kann.

          Wichtig ist, dass das File am Temporärort nicht erreichbar und nicht veränderbar ist durch Dritte.

          Und für den Fall, dass man den Temporärort nicht beeinflussen kann, was sind dann gescheite Maßnahmen, die die Sicherheit gegen Manipulation wärend des Upload- und Verarbeitungsprozesses erhöhen? Sollte man es erstmal so schnell wie möglich selbst in ein Quarantäne-Verzeichnis verschieben? Oder ist es gar nicht unbedingt erforderlich, weil man ja sowieso noch eine Plausibilitätskontrolle nachschiebt?

          Lo!

          1. Mein Script scheint soweit fertig zu sein. Das einzige Problem ist, dass existierende Bilder einfach überschrieben werden, wenn glaichnamige geuploaded werden.

            habe bis jetzt nur file_exist() gefunde, aber gibt es eine andere lösung?

            1. Hi!

              Das einzige Problem ist, dass existierende Bilder einfach überschrieben werden, wenn glaichnamige geuploaded werden.
              habe bis jetzt nur file_exist() gefunde, aber gibt es eine andere lösung?

              Was gefällt dir daran nicht? Einen Dateinamen selbst zu vergeben (vielleicht mit uniqid()), wäre eine Alternative.

              Lo!

              1. Hi!

                Das einzige Problem ist, dass existierende Bilder einfach überschrieben werden, wenn glaichnamige geuploaded werden.
                habe bis jetzt nur file_exist() gefunde, aber gibt es eine andere lösung?

                Was gefällt dir daran nicht? Einen Dateinamen selbst zu vergeben (vielleicht mit uniqid()), wäre eine Alternative.

                Lo!

                Nunja,

                if(file_exist($_FILES['image'][name])){
                     //und dann?
                }

                Ich meine, es gibt ja einfach False zurück fals noch keine Datei mit diesem Namen vorhanden ist. Und was passiert wenn schon?

                1. Hi!

                  Ich meine, es gibt ja einfach False zurück fals noch keine Datei mit diesem Namen vorhanden ist. Und was passiert wenn schon?

                  Na, das musst du entscheiden, was du willst. Ablehnen oder umbenennen.

                  Lo!

                2. Hello,

                  if(file_exist($_FILES['image'][name])){
                       //und dann?
                  }

                  ist im konkurrierenden Betrieb Bullshit.

                  Da zählt nur die Echtzeitkontrolle.

                  Liebe Grüße aus dem schönen Oberharz

                  Tom vom Berg

                  --
                   ☻_
                  /▌
                  / \ Nur selber lernen macht schlau
                  http://bergpost.annerschbarrich.de
          2. Hello,

            WEnn man die Vor- und Nachteile unterschiedlicher Speichertechniken vergleicht, sollte man doch auch ihre Nebenwirkungen erwähnen, oder? Und für die Vergangenheit planen und entwickeln wir doch nur noch selten (ich lebe davon von zu Zeit noch). Da sollte man dann also die 1.1-Methoden nicht unterschlagen...

            Und wo ist nun der Zwang, die genannten Features zu implementieren?

            Ach, ich dachte, es ginge um einen reelen Vergleich und nicht um Ideologie :-(

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Hi!

              WEnn man die Vor- und Nachteile unterschiedlicher Speichertechniken vergleicht, sollte man doch auch ihre Nebenwirkungen erwähnen, oder? Und für die Vergangenheit planen und entwickeln wir doch nur noch selten (ich lebe davon von zu Zeit noch). Da sollte man dann also die 1.1-Methoden nicht unterschlagen...
              Und wo ist nun der Zwang, die genannten Features zu implementieren?
              Ach, ich dachte, es ginge um einen reelen Vergleich und nicht um Ideologie :-(

              Du willst mich nicht verstehen, oder?. Du hast auch meine Frage mit einem anderen Zitat zusammengemischt, auf die sie sich gar nicht bezog.

              Du sagtest, ein Download-Script müsse unbedingt auch die HTTP-1.1-Features implementieren, und ich wollte wissen, warum das so sein müsse.

              Lo!

    3. ich sehe schon, es wird immer wichtiger, dass ich meinen Artikel im Wiki endlich mal fertig mache.

      Ich habe gerade meinen Kaffee auf die Tastatur gespuckt. Danke.

      1. Hello,

        ich sehe schon, es wird immer wichtiger, dass ich meinen Artikel im Wiki endlich mal fertig mache.

        Ich habe gerade meinen Kaffee auf die Tastatur gespuckt. Danke.

        Das freut mich.
        Dann werden die blöden Kommentare ja erstmal unterbleiben, bis die Tastatur wieder funktioniert :-P

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
  2. Hi!

    Ich würde gerne aus einem Formular heraus einen Bilderupload zulassen. Nun kam ich auf die idee via BLOB bilder in die DB zu speichern, was aber anscheinend nicht sonderlich toll ist.

    Kommt drauf an. Man kann auch Vorteile daraus ziehen, je nachdem welche der Eigenschaften man wie wertet.

    Beispiel Script von php.net:

    Das ist nur sehr rudimentär, da fehlt so einiges. Als erstes sollte man feststellen, ob beim Upload Fehler aufgetreten sind, was man durch Sicherstellen seiner Existenz und anschließendem Befragen des Wertes von $_FILES[...]['error'] in Erfahrung bringen kann. Ansonsten kann man sich ja das Fortfahren sparen.

    $uploaddir -> wie wird diese genau definiert? Bzw wenn ich ein Ordner, am selben ort mache wie das script laufen soll, kann ich dann einfach "ordner/" eintragen?

    Kann, ja, ob das sinnvoll ist, muss noch geklärt werden.

    mit dem move_uploaded_file wird das bild aus dem tmp verzeichnis in meinen ordner hinein verschoben, kann ich also vorher noch einige bedingungen setzen, zB solche ergänzungen:

    Ja, nicht nur "kann", sondern auch "sollte". Die Größe ist nur ein relativ nebensächliches Kriterium. Viel wichtiger ist, dass man dir auch Scripts und ähnliches Zeug hochladen kann. Wenn das im Upload-Verzeichnis landet und man das wunderbar wieder abrufen kann, nebst der Ausführung von darin enthaltenem Code, dann war das mal dein Server. Gegenmaßnahmen wären, die Ausführung von Scripten in dem Verzeichnis verhindern. Bedenke, dass neben PHP auch noch andere Dinge aktiviert sein können. Prüfen oder explizites Setzen einer Dateiendung, die den Webserver nicht zum Interpretieren des Inhalts veranlasst, sowie Prüfen des Dateiinhaltes. Bei den üglichen Grafikformaten sollte getimagesize() ein gültiges Ergebnis liefern. Letzteres allein reicht aber nicht, denn man kann auch in gültig aussehenden Grafikdateien Programmcode unterbringen, der dann doch wegen einer durchgelassenen Dateiendung ausgeführt wird.

    Bezüglich der URL, wo bekomme ich die her? Ich will ja in eine DB eine Id den Namen und die url speichern. Aber wo bekomme ich die url eig. her?

    Die ergibt sich genau wie bei deinem Script durch den Ablageort im Dateisystem, falls nicht irgendwelche Umschreibereien (mod_rewrite) stattfinden.

    Die Inserts in die DB kämen dementsprechend auch ins obige else oder? Denn der move_uploaded_file befehl wird ja nur ausgeführt fals keine errors auftreten.

    Oder. Nur wenn kein Fehler auftrat will man den Upload in der Datenhaltung verewigen. Aber durch die anderen Prüfungen wird das dann doch noch etwas umfangreicher als nur ein einfaches if-else.

    Lo!