Julius: Wiki: Dateiupload mit PHP

3 52

Wiki: Dateiupload mit PHP

Julius
  • php
  • selfhtml-wiki
  • sicherheit
  1. 0
    dedlfix
    1. 0
      Matthias Scharwies
      1. 2
        Julius
    2. 3
      Julius
      1. 1
        TS
        1. 0
          1unitedpower
          1. 1
            Julius
            1. 0
              1unitedpower
              1. 0
                TS
                1. 0
                  1unitedpower
          2. 0
            robertroth
            1. 3
              Raketenquellsuchsystem
              1. 0

                Wiki: Dateiupload mit PHP (Verständlichere Ergänzung)

                Raketenquellsuchsystem
              2. 2
                robertroth
                1. 0
                  Raketenquellsuchsystem
                2. 1
                  Rolf B
          3. 1
            TS
            1. 0
              1unitedpower
              1. 1
                TS
                1. 0
                  1unitedpower
                2. 1
                  robertroth
                  • idee
                  • php
                  • sicherheit
            2. 1
              Julius
              1. 2
                TS
                1. 2
                  Julius
                  1. 0
                    TS
            3. -1
              Raketenstarter
              1. -1
                Raketenstarter
      2. 0
        dedlfix
  2. 1
    Gunnar Bittersmann
    • grafik
    • grafik
    • selfhtml-wiki
    1. 0
      Der Martin
      1. 0

        webp-Konverter <.->

        Raketenwilli
        1. 0
          Der Martin
          • grafik
          • grafik
          1. 0
            Raketenwilli
            • produktinfo
            1. 0
              Der Martin
              • humor
              • produktinfo
          2. 0
            Raketenwilli
    2. 2
      dedlfix
  3. 2
    Matthias Apsel
    1. 2
      Julius
  4. 2
    Felix Riesterer
    1. 4
      Julius
  5. 0
    1unitedpower
    1. 1
      Julius
  6. 2
    Julius
    1. 0
      TS
      1. 1
        Julius
        1. 0
          TS
          1. 0
            Tabellenkalk
            1. 0
              TS
              1. 0
                raketenquelltextleser
                1. 1
                  Julius
                  • php
                  • sicherheit
              2. 1
                Julius

problematische Seite

Hallo an alle und Frohe Ostern!

Ich überarbeite gerade in meinem Benutzernamensraum (noch unvollständig, teilweise nur Stichpunkte) den Wiki-Artikel zum Thema Datei Upload mit PHP. Er ist unvollständig, zu umfangreich (drittlängster Artikel im gesamten Wiki!) und zu allgemein, um noch als Tutorial durchzugehen.

Mein Ansatz ist, anhand eines Bilder-Uploads im Stile eines Image-Hosters für die Probleme bei Dateiuploads zu sensibilisieren und eine möglichst einfache und dennoch sichere Lösung vorzustellen. Dabei treffe ich folgende Annahmen:

  • Man kann nur Bilder (JPEG, PNG und GIF) hochladen, diese werden auch mit mime_content_type überprüft (getimagesize bringt keinen Mehrwert, es wird explizit im Manual davon abgeraten, diese Funktion zur Überprüfung von Bildern zu verwenden). SVGs spare ich erst einmal aus, weil die zu riskant sind. Dafür gibt es validierende Libraries, nur erhöht das wieder die Komplexität des Ganzen.
  • Dateinamen werden zufällig erzeugt, man spart sich die ganzen Probleme mit Case-sensitivity, Zeichen oder ganzen Dateinamen mit spezieller Bedeutung.
  • Uploads werden außerhalb des Document-Roots gespeichert – gibt es eigentlich wirklich Webhoster, bei denen das nicht geht?
  • Bei der Auslieferung über ein PHP-Skript wird der Header X-Content-Type-Options: nosniff gesetzt.

Mir ist bewusst, dass dies eine ähnliche Diskussion wie mit dem Loginsystem Anno 2016 ist. Nur halte ich das Risiko hier für besser beherrschbar und irgendwo muss man auch mal erklären, wie man einen Dateiupload grundsätzlich sicher umsetzt. Das Web ist voll von halbgaren Lösungen, auf die sonst zurückgegriffen würde (diese ist davon noch die am wenigsten unsicherste – der Nutzer kann den Dateinamen bestimmen, Dateien werden direkt vom Webserver ausgeliefert, URL-Parameter mit dem Dateinamen landet ohne wirklich tiefgehende Überprüfung in einem HTTP-Header, bei Nichtvorhandensein der zur Überprüfung verwendeten Funktion exif_imagetype wird die Datei einfach ohne Überprüfung hochgeladen, MIME-Type wird nicht mittels mime_content_type o. ä. überprüft) und die sich auch selten korrigieren lassen, weil es sich nicht um Wikis handelt oder Autoren nicht reagieren (gelegentlich schreibe ich bei so etwas die Autoren an, aber meist ist das schlicht Zeitverschwendung).

Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

Gruß
Julius

  1. problematische Seite

    Tach!

    • Dateinamen werden zufällig erzeugt, man spart sich die ganzen Probleme mit Case-sensitivity, Zeichen oder ganzen Dateinamen mit spezieller Bedeutung.

    Der zufällig erzeugte Name und der im Upload übergebene Name sollten am Ende übergeben werden. Der Verwender möchte letzteren vielleicht nicht verlieren. Andererseits vergibt PHP bereits einen zufälligen temporären Namen, den man weiterverwenden kann, auch wenn man die Datei anderswohin kopiert. Die Wahrscheinlichkeit sollte ausreichend gering sein, dass der Name mehrfach vergeben wird. Gegebenenfalls muss nur eine Endung hinzugefügt werden, damit der Webserver sie mit richtigem Content-Type behandeln kann.

    Nochmal andererseits kann man das Kopieren beispielhaft zeigen, denn mehr als ein move_uploaded_file() ist das am Ende nicht, oder doch? Der Verwender muss das sowieso anhand seiner konkreten Anforderungen lösen.

    • Uploads werden außerhalb des Document-Roots gespeichert – gibt es eigentlich wirklich Webhoster, bei denen das nicht geht?

    Welche Verzeichnisse außerhalb vom DocumentRoot liegen und beschrieben werden können, ist individuell. Ob der Verwender das generell außerhalb haben möchte, ist auch nicht gewiss. Angenommen du schreibt eine Funktion, die sich um das Behandeln des Uploads kümmert, und dazu ein move_uploaded_file() ausführt, dann kann es sein, dass der Verwender die Datei nochmal kopieren muss, weil er sie woanders ablegen möchte. Andererseits ist es wie erwähnt sowieso individuell, wo was abgelegt werden kann respektive soll, so dass du im Falle einer Funktion einen Parameter entgegennehmen müsstest, der den Zielpfad angibt. Aber wenn du nichts weiter als move_uploaded_file() zuzüglich vorheriger Prüfungen ausführst, dann kann diesen letzten Schritt auch der Verwender selbständig gehen. Es reicht dann, nur zu zeigen, welche Prüfungen erforderlich sind.

    Das Web ist voll von halbgaren Lösungen, auf die sonst zurückgegriffen würde (diese ist davon noch die am wenigsten unsicherste [...]

    Die Fehlerbehandlung mit die() führt zu einer unakzeptablen UX. Das kann man so nicht bieten. Andererseits kann man auch nicht so allgemein eine bessere Variante zeigen, die allen Ansprüchen und Gegebenheiten gerecht wird. Exceptions werfen oder eine Liste mit Fehlermeldungen zurückgeben wären Alternativen, bei denen der Verwender individuell reagieren kann.

    Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

    Das kommt ganz darauf an, ob man sich einigen kann, welche Qualität der Artikel haben soll.

    dedlfix.

    1. problematische Seite

      Servus!

      Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

      Das kommt ganz darauf an, ob man sich einigen kann, welche Qualität der Artikel haben soll.

      Wenn ich mir die Qualität der Beiträge von @Julius im Wiki anschaue, habe ich keine Befürchtungen.

      Herzliche Grüße

      Matthias Scharwies

      --
      25 Jahre SELFHTML → SELF-Treffen 05.-07. Juni 2020 in Mannheim
      1. problematische Seite

        Hallo Matthias,

        Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

        Das kommt ganz darauf an, ob man sich einigen kann, welche Qualität der Artikel haben soll.

        Wenn ich mir die Qualität der Beiträge von @Julius im Wiki anschaue, habe ich keine Befürchtungen.

        Vielen Dank für das Vertrauen 😀.

        Gruß
        Julius

    2. problematische Seite

      Hallo dedlfix,

      • Dateinamen werden zufällig erzeugt, man spart sich die ganzen Probleme mit Case-sensitivity, Zeichen oder ganzen Dateinamen mit spezieller Bedeutung.

      Der zufällig erzeugte Name und der im Upload übergebene Name sollten am Ende übergeben werden. Der Verwender möchte letzteren vielleicht nicht verlieren. Andererseits vergibt PHP bereits einen zufälligen temporären Namen, den man weiterverwenden kann, auch wenn man die Datei anderswohin kopiert. Die Wahrscheinlichkeit sollte ausreichend gering sein, dass der Name mehrfach vergeben wird. Gegebenenfalls muss nur eine Endung hinzugefügt werden, damit der Webserver sie mit richtigem Content-Type behandeln kann.

      Die Vergabe der richtigen Endung ist geplant. Was die Eindeutigkeit des Dateinamens angeht, habe ich tatsächlich auch schon über die Weiterverwendung des temporären Namens (ggf. plus ein paar extra-Zeichen) nachgedacht, denn PHP wird bei mehreren gleichzeitig durchgeführten Dateiupload wohl kaum mehrmals den selben Namen vergeben, sodass man sich wohl eher kein Time-of-Check-to-Time-of-Use-Problem einhandeln dürfte. Mir gefällt nicht, dass move_uploaded_file keine Möglichkeit bietet, den gewünschten Dateinamen vor dem Verschieben irgendwie zu „reservieren“. Vorab kann ich natürlich prüfen, ob die angedachte Datei bereits existiert (sicher ist sicher), aber eben leider nicht sperren. Dieses Problem ist mir aufgefallen und TS beim Schreiben des ursprünglichen Artikels offensichtlich auch. Nur sonst konnte ich nichts dazu finden, weshalb ich mich frage, ob das überhaupt tatsächlich ein Problem darstellt.

      • Uploads werden außerhalb des Document-Roots gespeichert – gibt es eigentlich wirklich Webhoster, bei denen das nicht geht?

      Welche Verzeichnisse außerhalb vom DocumentRoot liegen und beschrieben werden können, ist individuell. Ob der Verwender das generell außerhalb haben möchte, ist auch nicht gewiss.

      Irgendwie muss man sicherstellen, dass der PHP-Interpreter sich nicht für die hochgeladenen Dateien interessiert (ja, das ist trotz der richtigen Dateiendung bei schlampiger Konfiguration des Webservers möglicherweise ein Problem). Außerhalb des Document-Roots zu speichern erschien mir da die sicherste Lösung. Wenn denn das ausliefernde Skript den übergebenen Dateinamen korrekt behandelt...
      Ich sollte mal in den Quellcode von WordPress gucken, wie die das lösen. Sollte das tatsächlich ein solches Problem sein, dann müssten die sich ja bei der Verbreitung damit beschäftigt haben.

      Ich werde wohl nicht darum herum kommen, beide Ansätze und deren Fallstricke genau zu erklären. Die Konfiguration des Webservers erwähne ich nur, das ist definitiv ein Thema für sich und individuell.

      Angenommen du schreibt eine Funktion, die sich um das Behandeln des Uploads kümmert, und dazu ein move_uploaded_file() ausführt, dann kann es sein, dass der Verwender die Datei nochmal kopieren muss, weil er sie woanders ablegen möchte.

      Guter Punkt! So ein Gebastel habe ich tatsächlich kürzlich in einem Projekt gefunden: base64-kodierte Daten werden als Parameter entgegengenommen, dekodiert und als Datei gespeichert, die selbe Datei drei Zeilen später eingelesen und gelöscht, deren Inhalt wieder base64-kodiert und per E-Mail verschickt 🤦.

      Andererseits ist es wie erwähnt sowieso individuell, wo was abgelegt werden kann respektive soll, so dass du im Falle einer Funktion einen Parameter entgegennehmen müsstest, der den Zielpfad angibt. Aber wenn du nichts weiter als move_uploaded_file() zuzüglich vorheriger Prüfungen ausführst, dann kann diesen letzten Schritt auch der Verwender selbständig gehen. Es reicht dann, nur zu zeigen, welche Prüfungen erforderlich sind.

      Den Weg werde ich auch gehen, Danke! Es nimmt Komplexität aus dem Code, schafft Raum für Erklärungen und ermuntert den Leser des Tutorials zum Nachdenken über seine Anforderungen.

      Das Web ist voll von halbgaren Lösungen, auf die sonst zurückgegriffen würde (diese ist davon noch die am wenigsten unsicherste [...]

      Die Fehlerbehandlung mit die() führt zu einer unakzeptablen UX. Das kann man so nicht bieten. Andererseits kann man auch nicht so allgemein eine bessere Variante zeigen, die allen Ansprüchen und Gegebenheiten gerecht wird. Exceptions werfen oder eine Liste mit Fehlermeldungen zurückgeben wären Alternativen, bei denen der Verwender individuell reagieren kann.

      Danke! Aus der UX-Perspektive habe ich die Verwendung von die() tatsächlich noch nicht betrachtet. Mit die() schnell das Programm zu beenden, ist für den Entwickler vermutlich einfach die am schnellsten hingerotztbare „Lösung“. Meine angedachte Lösung habe ich bereits intuitiv mit einer solchen Liste ausgestattet und überlegt, welche der Fehlermeldungen dem Nutzer wie vermittelt werden sollen. Dass z. B. eine PHP-Extension den Upload abgebrochen hat, interessiert den Entwickler, aber nicht den Nutzer. Der muss nur wissen, dass es nicht geklappt hat.

      Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

      Das kommt ganz darauf an, ob man sich einigen kann, welche Qualität der Artikel haben soll.

      Klar, wenn ich Müll oder Unpassendes schreibe und mir das begründet wird, werde ich keinen Aufstand machen, wenn das weg kommt. Ich bemühe mich um Qualität und bin um Hilfe auf dem Weg dorthin dankbar 😀.

      Gruß
      Julius

      1. problematische Seite

        Hello,

        [•••]

        move_uploaded_file() gehört mMn schon lange auf die depricated-Liste. Es ist noch entstanden zur Zeit der unsicheren globalen Arrays. Mit der Einführung von $_FILES wurde aber der temporäre Name des hochgeladenen Files abgesichert. Damit wurde die Sicherheitslücke, die move_uploaded_file() vorher schließen sollte, beseitigt.

        Daher ist die von mir vorgestellte Funktion save_uploaded_file() auf jeden Fall zielführender. Sie ermöglicht das TOCTTOU-freie Speichern von Dateien mit Statusrückmeldung, und insbesondere erspart sie beim Zusammenwirken mit Datenbanken an dieser Stelle wieder Overhead. •••

        Glück Auf
        Tom vom Berg

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
        1. problematische Seite

          move_uploaded_file() gehört mMn schon lange auf die depricated-Liste. Es ist noch entstanden zur Zeit der unsicheren globalen Arrays. Mit der Einführung von $_FILES wurde aber der temporäre Name des hochgeladenen Files abgesichert. Damit wurde die Sicherheitslücke, die move_uploaded_file() vorher schließen sollte, beseitigt.

          Von welcher Sicherheitslücke sprichst du? Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann. Es ist dann Aufgabe der Anwendung die Datei zu überprüfen (bspw. auf Mime-Type oder Viren) bevor sie aus der Sandbox an eine andere Stelle verschoben wird. Die normalen PHP Dateisystem-Funktionen haben ggf. keinen Zugriff auf das Upload-Verzeichnis, wenn es außerhalb des open_basedir-Verzeichnisbaums liegt. Deshalb braucht man move_uploaded_file als privilegierte Funktion.

          Daher ist die von mir vorgestellte Funktion save_uploaded_file() auf jeden Fall zielführender. Sie ermöglicht das TOCTTOU-freie Speichern von Dateien mit Statusrückmeldung

          Ich habe gerade im PHP Sourcecode nachgesehen, move_uploaded_file benutzt unter der Haube auch den Filelock-Mechanismus. Man muss ein bisschen graben bis man auf diese beiden Zeilen stößt:

          srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx)
          
          deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
          

          Der zweite Parameter ist der File-Mode.

          und insbesondere erspart sie beim Zusammenwirken mit Datenbanken an dieser Stelle wieder Overhead. •••

          Wie das?

          1. problematische Seite

            Hallo 1unitedpower,

            Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann. Es ist dann Aufgabe der Anwendung die Datei zu überprüfen (bspw. auf Mime-Type oder Viren) bevor sie aus der Sandbox an eine andere Stelle verschoben wird.

            Notiz an mich selbst: Dieses Prinzip muss ich in dem Tutorial nicht nur implizit vorführen, sondern auch noch einmal explizit erklären.

            Die normalen PHP Dateisystem-Funktionen haben ggf. keinen Zugriff auf das Upload-Verzeichnis, wenn es außerhalb des open_basedir-Verzeichnisbaums liegt. Deshalb braucht man move_uploaded_file als privilegierte Funktion.

            Guter Punkt, das hatte ich auch nicht auf dem Schirm, werde ich einpflegen!

            Daher ist die von mir vorgestellte Funktion save_uploaded_file() auf jeden Fall zielführender. Sie ermöglicht das TOCTTOU-freie Speichern von Dateien mit Statusrückmeldung

            Ich habe gerade im PHP Sourcecode nachgesehen, move_uploaded_file benutzt unter der Haube auch den Filelock-Mechanismus. Man muss ein bisschen graben bis man auf diese beiden Zeilen stößt:

            srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx)
            
            deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
            

            Der zweite Parameter ist der File-Mode.

            An der Stelle im Source-Code war ich auf meiner Recherche tatsächlich auch schon mal, glaube ich, habe aber nicht erkannt, dass das die Dateien lockt. Aus Interesse: Woran machst du das konkret fest? – Am Suffix _ex, das für exclusive stehen könnte?

            Gruß
            Julius

            1. problematische Seite

              srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx)
              
              deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
              

              An der Stelle im Source-Code war ich auf meiner Recherche tatsächlich auch schon mal, glaube ich, habe aber nicht erkannt, dass das die Dateien lockt. Aus Interesse: Woran machst du das konkret fest? – Am Suffix _ex, das für exclusive stehen könnte?

              Das hatte ich vermutet, aber nach kurzer Recherche steht das _ex wohl für Stream Context − offensichtlich. Ich habe dann nochmal versucht tiefer in den Code einzusteigen, aber ohne Erfolg, dafür reichen meine C-Kenntnisse nicht aus. Ich nehme meine Aussage über das Locking zurück.

              1. problematische Seite

                Hello,

                srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx)
                
                deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
                

                An der Stelle im Source-Code war ich auf meiner Recherche tatsächlich auch schon mal, glaube ich, habe aber nicht erkannt, dass das die Dateien lockt. Aus Interesse: Woran machst du das konkret fest? – Am Suffix _ex, das für exclusive stehen könnte?

                Das hatte ich vermutet, aber nach kurzer Recherche steht das _ex wohl für Stream Context − offensichtlich. Ich habe dann nochmal versucht tiefer in den Code einzusteigen, aber ohne Erfolg, dafür reichen meine C-Kenntnisse nicht aus. Ich nehme meine Aussage über das Locking zurück.

                Das Locking war ja eigentlich auch nicht das Thema. Das sollte sich bei ordentlicher Programmierung von konkurrierenden Prozessen von alleine verstehen! Und nach POSIX müssen Streams mit Locking arbeiten. Ich weiß aber von PHP (aus meinen Schmieraufzeichnungen von damals), dass es dies an dieser Stelle leider genau nicht beachtet hat. Da Du aber nicht dazugeschrieben hast, aus welcher Version der Quellcode stammte, wollte ich gar nicht tiefer darauf eingehen.

                Glück Auf
                Tom vom Berg

                --
                Es gibt nichts Gutes, außer man tut es!
                Das Leben selbst ist der Sinn.
                1. problematische Seite

                  Das Locking war ja eigentolich auch nicht das Thema.

                  Alles gut, das Missverständnis konnten wir ja aufklären.

                  Da Du aber nicht dazugeschrieben hast, aus welcher Version der Quellcode stammte, wollte ich gar nicht tiefer darauf eingehen.

                  Du kannst bei GitHub einfach auf den Blame-Button drücken, um herauszufinden, wann der Code offiziell Einzug ins Repository gefunden hat. Und du erfährst auch gleich, wen du dafür die Schuld geben darfst - insofern ein sehr treffender Name für die Funktion.

          2. problematische Seite

            Liebe Mitdenker, liebe Wissende, liebe Neugierige,

            Ich habe gerade im PHP Sourcecode nachgesehen, move_uploaded_file benutzt unter der Haube auch den Filelock-Mechanismus. Man muss ein bisschen graben bis man auf diese beiden Zeilen stößt:

            srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx)
            
            deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
            

            Der zweite Parameter ist der File-Mode.

            Das ist interessant.

            Kannst Du eventuell auch sagen, wie die Funktion dafür gesorgt hat, dass nur gerade in dieser Instanz hochgeladene Dateien verschoben werden konnten, und nicht aus Versehen oder auch absichtlich fremde?

            Euer Robert

            --
            Möge der Forumsgeist ewig leben!
            1. problematische Seite

              Kannst Du eventuell auch sagen, wie die Funktion dafür gesorgt hat, dass nur gerade in dieser Instanz hochgeladene Dateien verschoben werden konnten, und nicht aus Versehen oder auch absichtlich fremde?

              https://www.php.net/manual/en/function.is-uploaded-file.php

              Zitat:

              Note that calling this function before move_uploaded_file() is not necessary, as it does the exact same checks already. It provides no extra security. Only when you're trying to use an uploaded file for something other than moving it to a new location.

              … der Rest findet sich (gegenwärtig) an der Adresse: https://github.com/php/php-src/blob/master/ext/standard/basic_functions.c#L2561

              (L2561 bedeutet Zeile 2561)

              1. problematische Seite

                PHP baut ja in $_FILES den Name der temporären Datei ein. Dieser wird beim Start des Skripts erzeugt und ist eindeutig. Die Funktionen move_uploaded_file() und is_uploaded_file() überprüfen, ob die als Argument angegebene Datei hochgeladen wurde, indem diese kurzerhand überprüfen, ob der Dateiname in $_FILES[*]['tmp_name'] enthalten ist. Wurde die Datei nicht in der Sitzung/Instanz hochgeladen, dann steht deren temporärer Name schlicht nicht drin.

                Ein Angreifer könnte übrigens einen Dateiname, den ein anderer hochgeladen hat durchaus erraten, denn das schwer aber sicherlich nicht unmöglich. Aber dazu braucht er a) lokale Informationen wie die temporären Namen der vorher hochgeladenen Dateien und vor allem die Möglichkeit, eine beliebige Datei auf dem System öffnen oder womöglich gar verändern zu können.

                Dabei kommen so viele Probleme zusammen (die veränderte Datei wird z.B. wieder gelöscht wenn das Skript des eigentlichen Hochladers beendet wird), dass ein Angriff darauf unbekannt ist.

                Die Voraussetzungen zum Lesen der Datei sind schon so hoch, dass man „ein ganz anderes Problem“ hat, wenn es auf dem System ein Skript gäbe, mit dem man beliebige Dateien lesen kann:

                Ein Skript mit etwas Dummen wie …

                <?=readfile( $_GET['FileName'] ); ?>
                

                … wird ja wohl hoffentlich keiner online stellen - oder?

              2. problematische Seite

                Liebe Mitdenker, liebe Wissende, liebe Neugierige,

                Kannst Du eventuell auch sagen, wie die Funktion dafür gesorgt hat, dass nur gerade in dieser Instanz hochgeladene Dateien verschoben werden konnten, und nicht aus Versehen oder auch absichtlich fremde?

                https://www.php.net/manual/en/function.is-uploaded-file.php

                Zitat:

                Note that calling this function before move_uploaded_file() is not necessary, as it does the exact same checks already. It provides no extra security. Only when you're trying to use an uploaded file for something other than moving it to a new location.

                … der Rest findet sich (gegenwärtig) an der Adresse: https://github.com/php/php-src/blob/master/ext/standard/basic_functions.c#L2561

                (L2561 bedeutet Zeile 2561)

                Ja, nee, is schon klar :-(

                    if (!SG(rfc1867_uploaded_files)) {
                		    RETURN_FALSE;
                    }
                

                Das ist vermutlich die Stelle, an der geprüft wird. Nur wie das Flag erzeugt wird, weiß ich davon leider immer noch nicht {traurig guck}.

                Was bedeutet eigentlich dieses "SG"? Was macht die Funktion?

                Spirituelle Grüße
                Euer Robert

                --
                Möge der Forumsgeist ewig leben!
                1. problematische Seite

                  Wo die genannte SG-Funktion gebaut wird habe ich auch nicht gefunden, was ich fand war ein "SourceGuardian", der macht aber was ganz anderes. Zudem finde ich SG() nicht besonders interessant, weil sich eigentlich aus dem Zusammenhang ergibt was die im Groben macht. Wie das nun genau gemacht wird finde ich - im Zusammenhang mit mit Deiner ursprünglichen - Frage derzeit „eher uninteressant“.

                  Ich glaube aber, für Dich ist etwas ganz anderes wissenswert:

                  Erst einmal ist es der Webserver, der mit dem Request die Datei bekommt, deren Name erzeugt und diese ablegt.

                  Lesestoff:

                  http://commons.apache.org/proper/commons-fileupload/apidocs/index.html

                  Wie ich schon geschrieben habe verwendet PHP den übergebenen Dateiname (und die Meta-Informationen wie originärer Dateiname, MimeType, Größe nur. Der Rest ist wie ich ihn beschrieben habe - PHP untersucht also nur den eigenen Hash (Array) $_FILES - und der wird, wenngleich in geeigneter Form, bei jedem Request vom Apache (andere Webserver wären ebenso zuständig) an das von (CGI) oder mit ihm gestartete (Modul) PHP übergeben.

                  Was die „geeignete Form“ ist, hängt davon ab, ob - nehmen wir PHP - das verarbeitende Programm als Modul oder CGI (genau dann gibt es eine definierte Schnittstelle) läuft.

                  Falls Du jetzt wissen willst, wie (im Prinzip) der Dateiname „gebaut“ wird, dann sieh bei PHP unter tmpfile() nach oder zieh dir auf einem Linux-System mit man tempfile rein was dort steht. Beides wrappt letztendlich die C-Lib-Funktion tmpfile. man 3 tmpfile zeigt Dir auf einem Linux-System was die macht.

                  Die Manuals finden sich auch an zahlreichen Stellen mit Suchmaschinen, weil man da mit sehr wenig EIGENER Arbeit sehr viel Text und Werbung online bringen kann.

                2. problematische Seite

                  Hallo robertroth,

                  nach Download des Sourcecodes und etwas Filesearch fördere ich dies in main/SAPI.h zu Tage:

                  BEGIN_EXTERN_C()
                  #ifdef ZTS
                  # define SG(v) ZEND_TSRMG_FAST(sapi_globals_offset, sapi_globals_struct *, v)
                  SAPI_API extern int sapi_globals_id;
                  SAPI_API extern size_t sapi_globals_offset;
                  #else
                  # define SG(v) (sapi_globals.v)
                  extern SAPI_API sapi_globals_struct sapi_globals;
                  #endif
                  

                  SAPI steht, wenn ich das richtig deute, für Server API. In SAPI.h ist eine struct namens _sapi_globals_struct, und darin befindet sich ein Eintrag namens rfc1867_uploaded_files. Aha!

                  Wie das abläuft, wenn ZTS definiert ist, weiß ich nicht, aber ohne diesen #define greift SG(foo) einfach auf sapi_globals.foo zu.

                  Rolf

                  --
                  sumpsi - posui - obstruxi
          3. problematische Seite

            Hello,

            move_uploaded_file() gehört mMn schon lange auf die depricated-Liste. Es ist noch entstanden zur Zeit der unsicheren globalen Arrays. Mit der Einführung von $_FILES wurde aber der temporäre Name des hochgeladenen Files abgesichert. Damit wurde die Sicherheitslücke, die move_uploaded_file() vorher schließen sollte, beseitigt.

            Von welcher Sicherheitslücke sprichst du? Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann.

            Das gilt (im shared Hosting, Modul) nur, wenn das Uploadverzeichnis pro Domain getrennt geführt wird. Anderenfalls kann der Inhalt der Datei auch von Anderen geändert werden, solange sie dort liegt.

            Es ist dann Aufgabe der Anwendung die Datei zu überprüfen (bspw. auf Mime-Type oder Viren) bevor sie aus der Sandbox an eine andere Stelle verschoben wird. Die normalen PHP Dateisystem-Funktionen haben ggf. keinen Zugriff auf das Upload-Verzeichnis, wenn es außerhalb des open_basedir-Verzeichnisbaums liegt. Deshalb braucht man move_uploaded_file als privilegierte Funktion.

            Das kann ich jetzt leider nicht so schnell überprüfen. Du meinst also, dass man das upload_tmp_dir außerhalb des open_basedir-Bereiches und der DocumentRoot legen kann, und der Upload trotzdem funktioniert? Und move_uploaded_file() darf dann als einzige PHP-Dateifunktion (vermutlich nur lesend?) auf dieses Verzeichnis und das Directory dieses Verzeichnisses (schreibend, zum Löschen) zugreifen?

            Daher ist die von mir vorgestellte Funktion save_uploaded_file() auf jeden Fall zielführender. Sie ermöglicht das TOCTTOU-freie Speichern von Dateien mit Statusrückmeldung

            Ich habe gerade im PHP Sourcecode nachgesehen, move_uploaded_file benutzt unter der Haube auch den Filelock-Mechanismus. Man muss ein bisschen graben bis man auf diese beiden Zeilen stößt:

            srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx)
            
            deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
            

            Der zweite Parameter ist der File-Mode.

            Darum ging es aber bei der ganzen Debatte gar nicht. Außerdem war die mögliche Sicherheitslücke, dass man aus Arglist einen fremden Dateinamen als Source untergeschoben bekommen hatte (in $HTTP_POST_FILES), duch Einführung von $_FILES erledigt.

            Also ist es möglich, auch eine eigene Move-, bzw. Save-Funktion zu erstellen, die auch sicher ist und den Zusatznutzen den von mir vorgestellten aufweist:

            function save_uploaded_file($source, $target, $overwrite=0)
            {
                # $overwrite == 0 -> no overwrite, only create if not exists
                # $overwrite == 1 -> open only if exists, overwrite, truncate!
                # $overwrite == 2 -> rewrite existing or create new
            
                # we believe that php will not produce 'lost handles' if we leave 
                # this function without closing them.
            
                if (!is_int($overwrite)) return 12;
            
                # open source
                if (!$fs = fopen($source, 'rb')) return 5;
                if (!flock($fs, LOCK_SH)) return 6; ## Source could not be locked;
            
                # open target
            
                switch ($overwrite) 
                {
                    case 0:  
                        if (!$ft = fopen($target, 'xb'))  return 3;  ## assumed 'already exists'
                    break;
            
                    case 1:
                        if (!$ft = fopen($target, 'rb+')) return 2;  ## assumed 'not found'
                    break;
            
                    case 2:
                        if (!$ft = fopen($target, 'wb'))  return 5;  ## assumed 'could not open'
                    break;
            
                    default:
                        return 12;
                }
            
                if (!flock($ft, LOCK_EX)) return 6;          ## Target could not be locked;
            
                $filesize = filesize($source);
                $cont = fread($fs, $filesize);
                if (strlen($cont) != $filesize) return 4;    
            
                fwrite($ft, $cont);
            
                fclose($fs);
            
                ## new file perhaps is shorter than old one
                ftruncate($ft, $filesize);
                fclose($ft);
                 
                return 0;
            
            }
            

            Sie überschreibt nicht einfach vorhandene Dateien im Zielverzeichnis, wenn man dies nicht wünscht. Geprüft wird dies TOCTTOU-sicher. Außerdem kann man die Funktion auch genau anders herum steuern, dass sie nur bereits vorhandene Dateien überschreibt, und keine neuen anlegt. Die müssen dann ggf. gekürzt werden. Und die dritte Variante ist die, mit der move_uploaded_file() auch arbeitet: plattmachen und/oder neu anlegen.

            In meiner obigen Funktion müsste noch das namensbasierte filesize() durch die passende handlebasierte Funktion fstat() ausgetauscht werden, damit sie konsequent handlebasiert ist. Die gab es damals noch nicht.

            und insbesondere erspart sie beim Zusammenwirken mit Datenbanken an dieser Stelle wieder Overhead. •••

            Wie das?

            Wenn ich Dateiinhalte nicht in der Datenbank speichern will (sollte man ja meistens nicht tun), sonden eben als Flatfiles, dann muss ich mir in der DB aber zumindest den Namen der Datei merken. Wenn der aber gleich bleiben kann, muss ich in der Datenbank nichts ändern. Ich erspare mir dort also den Schreibvorgang.

            Das ist nur ein Nebensache aus der Praxis mit einer Kontaktbörse, in der die User ständig ihre Bilder austauschen. Mach also bitte jetzt keine Hauptsache daraus.

            Glück Auf
            Tom vom Berg

            --
            Es gibt nichts Gutes, außer man tut es!
            Das Leben selbst ist der Sinn.
            1. problematische Seite

              Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann.

              Das gilt (im shared Hosting, Modul) nur, wenn das Uploadverzeichnis pro Domain getrennt geführt wird. Anderenfalls kann der Inhalt der Datei auch von Anderen geändert werden, solange sie dort liegt.

              Das ist ein zusätzlicher Aspekt.

              Es ist dann Aufgabe der Anwendung die Datei zu überprüfen (bspw. auf Mime-Type oder Viren) bevor sie aus der Sandbox an eine andere Stelle verschoben wird. Die normalen PHP Dateisystem-Funktionen haben ggf. keinen Zugriff auf das Upload-Verzeichnis, wenn es außerhalb des open_basedir-Verzeichnisbaums liegt. Deshalb braucht man move_uploaded_file als privilegierte Funktion.

              Das kann ich jetzt leider nicht so schnell überprüfen. Du meinst also, dass man das upload_tmp_dir außerhalb des open_basedir-Bereiches und der DocumentRoot legen kann, und der Upload trotzdem funktioniert? Und move_uploaded_file() darf dann als einzige PHP-Dateifunktion (vermutlich nur lesend?) auf dieses Verzeichnis und das Directory dieses Verzeichnisses (schreibend, zum Löschen) zugreifen?

              Genau.

              Ich habe gerade im PHP Sourcecode nachgesehen, move_uploaded_file benutzt unter der Haube auch den Filelock-Mechanismus. Man muss ein bisschen graben bis man auf diese beiden Zeilen stößt:

              Darum ging es aber bei der ganzen Debatte gar nicht. Außerdem war die mögliche Sicherheitslücke, dass man aus Arglist einen fremden Dateinamen als Source untergeschoben bekommen hatte (in $HTTP_POST_FILES), duch Einführung von $_FILES erledigt.

              Deshalb habe ich ja auch nochmal gefragt von welcher Lücke du sprichst. $HTTP_POST_FILES ist, soweit ich das erkennen kann, nur ein missbilligter Alias für $_FILES. Vielleicht war das früher mal anders.

              Also ist es möglich, auch eine eigene Move-, bzw. Save-Funktion zu erstellen, die auch sicher ist und den Zusatznutzen den von mir vorgestellten aufweist:

              Sie überschreibt nicht einfach vorhandene Dateien im Zielverzeichnis, wenn man dies nicht wünscht.

              Ja, das ist ein guter Punkt.

              und insbesondere erspart sie beim Zusammenwirken mit Datenbanken an dieser Stelle wieder Overhead. •••

              Wie das?

              Wenn ich Dateiinhalte nicht in der Datenbank speichern will (sollte man ja meistens nicht tun), sonden eben als Flatfiles, dann muss ich mir in der DB aber zumindest den Namen der Datei merken. Wenn der aber gleich bleiben kann, muss ich in der Datenbank nichts ändern. Ich erspare mir dort also den Schreibvorgang.

              Bei move_uploaded_file kannst du den Namen der Zieldatei auch frei wählen, da sehe ich jetzt keinen Vorteil.

              1. problematische Seite

                Hello,

                Wenn ich Dateiinhalte nicht in der Datenbank speichern will (sollte man ja meistens nicht tun), sonden eben als Flatfiles, dann muss ich mir in der DB aber zumindest den Namen der Datei merken. Wenn der aber gleich bleiben kann, muss ich in der Datenbank nichts ändern. Ich erspare mir dort also den Schreibvorgang.

                Bei move_uploaded_file kannst du den Namen der Zieldatei auch frei wählen, da sehe ich jetzt keinen Vorteil.

                Ja, das stimmt. Ich kann aber bei move_uploaded_file() nicht bestimmen, dass nur dann verschoben werden darf, wenn es die Zieldatei schon gibt. Meine Funktion kann dies aber. So ist es in dem besagten "Sozialarbeitsprojekt" für die Admins ganz einfach möglich, dem einen User zwanzig Bilder zu gestatten, der anderen aber nur fünf, ohne dafür die Datenbank bemühen zu müssen. Du glaubst mir hoffentlich, dass das die DB ganz mächtig entlastet hat. Not macht manchmal erfinderisch.

                Ach, und falls Du fragen wolltest, was der User denn macht, wenn er sein Bild löschen will? Dann kann er/sie ein Leerbild hochladen bzw. das ist schon "oben".

                Glück Auf
                Tom vom Berg

                --
                Es gibt nichts Gutes, außer man tut es!
                Das Leben selbst ist der Sinn.
                1. problematische Seite

                  Ja, das stimmt. Ich kann aber bei move_uploaded_file() nicht bestimmen, dass nur dann verschoben werden darf, wenn es die Zieldatei schon gibt.

                  Ja, das ist definitiv ein Vorteil deiner Variante. Für das Tutorial halte ich das diesen Anwendungsfall aber schon für zu spezifisch. Ich würde hier lieber eine Lösung sehen, die einfach einen generischen Dateinamen für den Zielpfad vergibt. Entweder den schon vorhandenen temporären Namen oder ein neu mit uniqid generierter Name. Die Kollisionsgefahr halte ich für ausreichend gering.

                2. problematische Seite

                  Lieber Tom,

                  Wenn ich Dateiinhalte nicht in der Datenbank speichern will (sollte man ja meistens nicht tun), sonden eben als Flatfiles, dann muss ich mir in der DB aber zumindest den Namen der Datei merken. Wenn der aber gleich bleiben kann, muss ich in der Datenbank nichts ändern. Ich erspare mir dort also den Schreibvorgang.

                  Bei move_uploaded_file kannst du den Namen der Zieldatei auch frei wählen, da sehe ich jetzt keinen Vorteil.

                  Ja, das stimmt. Ich kann aber bei move_uploaded_file() nicht bestimmen, dass nur dann verschoben werden darf, wenn es die Zieldatei schon gibt. Meine Funktion kann dies aber. So ist es in dem besagten "Sozialarbeitsprojekt" für die Admins ganz einfach möglich, dem einen User zwanzig Bilder zu gestatten, der anderen aber nur fünf, ohne dafür die Datenbank bemühen zu müssen. Du glaubst mir hoffentlich, dass das die DB ganz mächtig entlastet hat. Not macht manchmal erfinderisch.

                  Ach, und falls Du fragen wolltest, was der User denn macht, wenn er sein Bild löschen will? Dann kann er/sie ein Leerbild hochladen bzw. das ist schon "oben".

                  Das ist ein netter Trick, den ich mir merken muss.

                  Ich habe nämlich für meine Pflegedokusoftware das Problem, dass immer genau vorherbestimmte Dateien abgeliefert werden müssen und keine anderen eigenständig erstellt werden sollen.

                  So lässt sich das ausschließlich über das Dateisystem regeln, ohne die Datenbank quälen zu müssen.

                  Suuuper Idee!

                  Nur nebenbei: habt Ihr bei Euch da oben schon Corona-Fälle? Hier in Thüringen kenne ich persönlich noch niemand.

                  Spirituelle Grüße
                  Euer Robert

                  --
                  Möge der Forumsgeist ewig leben!
            2. problematische Seite

              Hallo TS,

              Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann.

              Das gilt (im shared Hosting, Modul) nur, wenn das Uploadverzeichnis pro Domain getrennt geführt wird. Anderenfalls kann der Inhalt der Datei auch von Anderen geändert werden, solange sie dort liegt.

              Hmm, und wenn das Sticky-Bit gesetzt und nur ich (bzw. mein mit meinen Benutzerrechten laufender PHP-Interpreter) die Datei lesen oder schreiben darf?

              Es ist dann Aufgabe der Anwendung die Datei zu überprüfen (bspw. auf Mime-Type oder Viren) bevor sie aus der Sandbox an eine andere Stelle verschoben wird. Die normalen PHP Dateisystem-Funktionen haben ggf. keinen Zugriff auf das Upload-Verzeichnis, wenn es außerhalb des open_basedir-Verzeichnisbaums liegt. Deshalb braucht man move_uploaded_file als privilegierte Funktion.

              Das kann ich jetzt leider nicht so schnell überprüfen. Du meinst also, dass man das upload_tmp_dir außerhalb des open_basedir-Bereiches und der DocumentRoot legen kann, und der Upload trotzdem funktioniert? Und move_uploaded_file() darf dann als einzige PHP-Dateifunktion (vermutlich nur lesend?) auf dieses Verzeichnis und das Directory dieses Verzeichnisses (schreibend, zum Löschen) zugreifen?

              Aus der PHP-Doku:

              move_uploaded_file() is both safe mode and open_basedir aware. However, restrictions are placed only on the destination path as to allow the moving of uploaded files in which filename may conflict with such restrictions. move_uploaded_file() ensures the safety of this operation by allowing only those files uploaded through PHP to be moved.

              Gruß
              Julius

              1. problematische Seite

                Hello Julius,

                Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann.

                Das gilt (im shared Hosting, Modul) nur, wenn das Uploadverzeichnis pro Domain getrennt geführt wird. Anderenfalls kann der Inhalt der Datei auch von Anderen geändert werden, solange sie dort liegt.

                Hmm, und wenn das Sticky-Bit gesetzt und nur ich (bzw. mein mit meinen Benutzerrechten laufender PHP-Interpreter) die Datei lesen oder schreiben darf?

                Oh heilige Sonne! Lang ists her. Ich habe mir damals genau diese Fragen gestellt und hoffe nun, auch heute noch die richtigen Antworten darauf zu kennen:

                Sticky-Bit sollte bei TMP-Verzeichnissen gesetzt sein. Dann kann nur der Handleinhaber die Datei löschen oder umbenennen. Es betrifft nämlich die Directory-Ebene.

                Nur der User darf ändern ist ja der Fall. Aber der Script-User hieß beim Apache-PHP-Modul immer gleich für alle Shared User (z.B. www-data). Erst ab Apache 2.4 ist es unter Solaris-OS (habe immer noch nicht ausprobiert, wo es noch funktioniert) möglich, einen VHostUserzu setzen. Da sind wir der Sicherheit dann schon ein Stück näher.

                Aber auch Du solltest nicht übersehen, dass meine Funktion save_uploaded_file() überhaupt nicht diesen Themenbereich im Hauptaugenmerk hatte, sondern die Möglichkeit der sauberen Zieladressierung.

                Glück Auf
                Tom vom Berg

                --
                Es gibt nichts Gutes, außer man tut es!
                Das Leben selbst ist der Sinn.
                1. problematische Seite

                  Hallo TS,

                  Sticky-Bit sollte bei TMP-Verzeichnissen gesetzt sein. Dann kann nur der Handleinhaber die Datei löschen oder umbenennen. Es betrifft nämlich die Directory-Ebene.

                  Gut zu wissen, das hatte ich falsch im Kopf. Ergibt ja auch Sinn so.

                  Nur der User darf ändern ist ja der Fall. Aber der Script-User hieß beim Apache-PHP-Modul immer gleich für alle Shared User (z.B. www-data).

                  Ah, das ist dann wieder die Sache, wie PHP mit dem Webserver verknüpft wurde. Bei meinem Webhoster (Uberspace) hat jeder Account seinen eigenen PHP-Prozess, daher hatte ich das nicht auf dem Schirm. Hmm, das wird langsam echt haarig, aber irgendwie kriege ich das auch noch in dem Artikel erklärt.

                  Erst ab Apache 2.4 ist es unter Solaris-OS (habe immer noch nicht ausprobiert, wo es noch funktioniert) möglich, einen VHostUserzu setzen. Da sind wir der Sicherheit dann schon ein Stück näher.

                  ... dann ist es aufgrund der Berechtigungen und des Sticky-Bits tatsächlich so, dass die Datei im /tmp-Verzeichnis nicht manipuliert werden kann, richtig?

                  Aber auch Du solltest nicht übersehen, dass meine Funktion save_uploaded_file() überhaupt nicht diesen Themenbereich im Hauptaugenmerk hatte, sondern die Möglichkeit der sauberen Zieladressierung.

                  Keine Sorge, das ist nicht passiert. Das kann ich auseinander halten. Die hier so nebenbei aufgekommenen Themen hatte ich eh schon auf meiner Noch-zu-klären-Liste stehen, insofern ist es gut, dass sie hier auf den Tisch kamen. Nebenbei habe ich auch noch verstanden, wie genau die von dir vorgestellte Funktion sich von move_uploaded_file unterscheidet, das war mir vorher nicht klar.

                  Gruß
                  Julius

                  1. problematische Seite

                    Hello Julius,

                    Nur der User darf ändern ist ja der Fall. Aber der Script-User hieß beim Apache-PHP-Modul immer gleich für alle Shared User (z.B. www-data).

                    Ah, das ist dann wieder die Sache, wie PHP mit dem Webserver verknüpft wurde. Bei meinem Webhoster (Uberspace) hat jeder Account seinen eigenen PHP-Prozess, daher hatte ich das nicht auf dem Schirm. Hmm, das wird langsam echt haarig, aber irgendwie kriege ich das auch noch in dem Artikel erklärt.

                    Erst ab Apache 2.4 ist es unter Solaris-OS (habe immer noch nicht ausprobiert, wo es noch funktioniert) möglich, einen VHostUserzu setzen. Da sind wir der Sicherheit dann schon ein Stück näher.

                    ... dann ist es aufgrund der Berechtigungen und des Sticky-Bits tatsächlich so, dass die Datei im /tmp-Verzeichnis nicht manipuliert werden kann, richtig?

                    Jedenfalls nur durch den PHP-User des virtuellen HTTP-Hosts. Wenn die Software anderswo Löcher hat (ähnlich der von Jörg zitierten GET-Lücke), dann könnten sich die Enduser der Applikationen auf dieser Domain trotzdem gegenseitig die Daten manipulieren. Bei sauberer Programmierung würde ich auch behaupten, dass es dann bezüglich PHP sicher sei. Ich weiß leider nicht, unter welchem User mit welcher Umask PHP die Tempdatei dann dort ablegt. Ich vermute mal Domainuser/Domainuser_(trw- --- ---). Dann könnten andere Applikationen in anderen Sprachen auch nicht mehr zugreifen.

                    Glück Auf
                    Tom vom Berg

                    --
                    Es gibt nichts Gutes, außer man tut es!
                    Das Leben selbst ist der Sinn.
            3. problematische Seite

              Von welcher Sicherheitslücke sprichst du? Das Upload-Verzeichnis dient gewissermaßen als Sandbox: die Datei wird erstmal an eine Stelle geschoben, an der sie nicht viel Schaden anrichten kann.

              Das gilt (im shared Hosting, Modul) nur, wenn das Uploadverzeichnis pro Domain getrennt geführt wird. Anderenfalls kann der Inhalt der Datei auch von Anderen geändert werden, solange sie dort liegt.

              Äh. Die temporäre Datei wird beim Fileupload mit den Rechten 0600 (rw----) abgelegt. (Nicht nur) Beim shared hosting (sondern auch zur Isolation gewisser Skriptsammlungen wie Wordpress) sollte mittels mod_suexec bzw. mod_fcgid sicher gestellt werden, dass die Skripte z.B. mit den Rechten eines (zu konfigurierenden) Eigentümers:Gruppe der jeweiligen Dateien/Verzeichnisse ausgeführt werden. Dann kann nur der Eigentümer bzw. die unter dessen Rechte laufenden Skripte auf die hoch geladene Datei zugreifen.

              Die Nutzung von suexec und fcgid hat freilich die Folge, dass z.B. PHP nicht als Modul, sondern in der CGI-Variante laufen „muss“. Verschiedene PHP-Versionen auf einem Server sind übrigens gar kein Problem.

              1. problematische Seite

                Die Abwertung gab es offenbar offensichtlich nur wegen der Erwähnung „gewisser Skriptsammlungen wie Wordpress“.

                Sonst hätte „man“ das begründet.

      2. problematische Seite

        Tach!

        • Uploads werden außerhalb des Document-Roots gespeichert – gibt es eigentlich wirklich Webhoster, bei denen das nicht geht?

        Welche Verzeichnisse außerhalb vom DocumentRoot liegen und beschrieben werden können, ist individuell. Ob der Verwender das generell außerhalb haben möchte, ist auch nicht gewiss.

        Irgendwie muss man sicherstellen, dass der PHP-Interpreter sich nicht für die hochgeladenen Dateien interessiert (ja, das ist trotz der richtigen Dateiendung bei schlampiger Konfiguration des Webservers möglicherweise ein Problem). Außerhalb des Document-Roots zu speichern erschien mir da die sicherste Lösung. Wenn denn das ausliefernde Skript den übergebenen Dateinamen korrekt behandelt...
        Ich sollte mal in den Quellcode von WordPress gucken, wie die das lösen. Sollte das tatsächlich ein solches Problem sein, dann müssten die sich ja bei der Verbreitung damit beschäftigt haben.

        Viele der großen Projekte haben das Problem, dass sie die Verzeichnisstruktur nicht kennen, und nicht davon ausgehen können, dass bei allen Nutzern Verzeichnisse außerhalb des DocumentRoots existieren. Per Default sind deren Upload-Verzeichnisse üblicherweise innerhalb. Nicht dass ich das gut finde, aber das ist sicher ein Komprimiss, um maximale Laufbarkeit sicherzustellen. Das Upload-Verzeichnis ist ja nur eines. Auch reine Library-Verzeichnisse werden im DocumentRoot abgelegt, und dann in jede Datei am Anfang ein die()-Schutz eingebaut, falls sie direkt aufgerufen wird. Nicht bei allen Projekten kann man das konfigurieren. Es ist auch nicht sinnvoll, die Abrufe generell durch ein Script zu schicken, wenn der Webserver das ansonsten ohne Script direkt und performanter ausliefern könnte. Ich denke, hier muss man den Verwendungszweck berücksichtigen, den du bei einem allgemeinen Artikel zum Dateiupload nicht kennen kannst.

        dedlfix.

  2. problematische Seite

    @@Julius

    Man kann nur Bilder (JPEG, PNG und GIF) hochladen

    Warum keine WebP?

    🖖 Stay hard! Stay hungry! Stay alive! Stay home!

    --
    Home Office ist so frustierend, weil man jetzt noch viel stärker bemerkt mit wievielen Menschen man zu tun hat, die nicht sinnerfassend lesen können. (@Grantscheam)
    1. problematische Seite

      Hallo,

      Man kann nur Bilder (JPEG, PNG und GIF) hochladen

      Warum keine WebP?

      apropos WebP: Gibt's eigentlich ein kleines Tool, gern auch konsolenbasiert, mit dem man die verlustarm in ein etabliertes Format (z.B. JPEG oder PNG) konvertieren kann?

      Denn meine Browser (Pale Moon, Firefox) können sie zwar anzeigen, aber sonst kaum ein Programm[1]. Von Erzeugen gar nicht zu reden. Das einzige, das ich zum Anzeigen oder Konvertieren gefunden habe, ist XNView (Multi-Platform). Das ist dann aber schon die ganz schwere Keule.

      Solange WebP also ausschließlich im Web (bzw. im Browser) nutzbar ist, halte ich das noch nicht für allgemein brauchbar.

      Live long and pros healthy,
       Martin

      --
      Ich stamme aus Ironien, einem Land am sarkastischen Ozean.

      1. Plattform: Linux Mint, Ubuntu-basiert ↩︎

      1. apropos WebP: Gibt's eigentlich ein kleines Tool, gern auch konsolenbasiert, mit dem man die verlustarm in ein etabliertes Format (z.B. JPEG oder PNG) konvertieren kann?

        sudo apt install webp
        …
        Die folgenden NEUEN Pakete werden installiert:
          webp
        0 aktualisiert, 1 neu installiert, 0 zu entfernen und 3 nicht aktualisiert.
        Es müssen 78,5 kB an Archiven heruntergeladen werden.
        Nach dieser Operation werden 275 kB Plattenplatz zusätzlich benutzt.
        

        Her:

        dwebp file.webp -o file.png
        

        Hin:

        cwebp input_file -o output_file.webp
        
        1. Hallo,

          apropos WebP: Gibt's eigentlich ein kleines Tool, gern auch konsolenbasiert, mit dem man die verlustarm in ein etabliertes Format (z.B. JPEG oder PNG) konvertieren kann?

          sudo apt install webp
          …
          Die folgenden NEUEN Pakete werden installiert:
            webp
          0 aktualisiert, 1 neu installiert, 0 zu entfernen und 3 nicht aktualisiert.
          Es müssen 78,5 kB an Archiven heruntergeladen werden.
          Nach dieser Operation werden 275 kB Plattenplatz zusätzlich benutzt.
          

          bei mir wird als dependency zusätzlich freeglut3 installiert. Aber sei's drum, ich probier das mal aus, danke für den Tip.

          Die Beschreibung des Pakets webp gibt übrigens keinen Hinweis auf den tatsächlichen Nutzwert:

          Image Compression format, based on the VP8 codec.
          WebP uses the modern VP8 compression format to deliver efficient compression of images for the web. More than 30% extra gain over optimized JPEG, for same quality, is not unusual.

          Nicht sehr aussagekräftig, oder?

          Live long and pros healthy,
           Martin

          --
          Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
          1. Die Beschreibung des Pakets webp gibt übrigens keinen Hinweis auf den tatsächlichen Nutzwert:

            Image Compression format, based on the VP8 codec.
            WebP uses the modern VP8 compression format to deliver efficient compression of images for the web. More than 30% extra gain over optimized JPEG, for same quality, is not unusual.

            Nicht sehr aussagekräftig, oder?

            Naja. Google meint wohl, es reiche auf der Webseite noch mehr Werbung zu veröffentlichen.

            Aber auch sonst ärgert man sich quasi jeden Tag mehr darüber, dass die wirklich wichtigen (und nicht ohnehin offensichtlichen Informationen) gerne versteckt werden: Zur Not unter einem Scanner-Label auf der Blisterpackung. Aber selbst ohne das wären die in 0.7mm Schrifthöhe und gelbroter Frabe auf orangem Hintergrund gedruckt…

            Im Fall von webp und linux kann man es wenigstens ausprobieren… und ggf. schadlos löschen.

            Hinweis: Die zahlreichen Onlinetools benutzen im Hintergrund genau dieses Paket...

            1. Hallo,

              Nicht sehr aussagekräftig, oder?

              Naja. Google meint wohl, es reiche auf der Webseite noch mehr Werbung zu veröffentlichen.

              aber das muss man auch erstmal finden.

              Aber auch sonst ärgert man sich quasi jeden Tag mehr darüber, dass die wirklich wichtigen (und nicht ohnehin offensichtlichen Informationen) gerne versteckt werden: Zur Not unter einem Scanner-Label auf der Blisterpackung.

              Ja, die Unsitte stößt mir auch sauer auf: Anstatt eines kleinen lesbaren Hinweises ein QR-Code. Selber schuld, wer kein Smartphone hat, um den abzufotografieren und direkt zur Webseite zu kommen.

              Aber selbst ohne das wären die in 0.7mm Schrifthöhe und gelbroter Frabe auf orangem Hintergrund gedruckt…

              Oder hellgrau auf hellhellgrau, wenn man sich an Windows orientiert. (Credits: Linuchs)

              Im Fall von webp und linux kann man es wenigstens ausprobieren… und ggf. schadlos löschen.

              Unlike Windows, usually.

              Live long and pros healthy,
               Martin

              --
              Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
          2. freeglut3

            Ja. „GLUT ist eine vom Fenstersystem unabhängige Werkzeugsammlung“. Das Zeug dient also dazu, dem vwebp (dem zugehörigen Fileviewer) ein Fenster zu verpassen.

    2. problematische Seite

      Tach!

      Man kann nur Bilder (JPEG, PNG und GIF) hochladen

      Warum keine WebP?

      Warum ist die Frage auf WebP beschränkt?

      Beim Programmieren muss man die Lösung nicht von vornherein für sämtliche Werte erstellen. Vielmehr kommt es darauf an, sie so generisch zu gestalten, dass man sie erweitern kann. Die Frage ist also nicht, welche konkreten Formate in der beispielhaften Lösung verwendet werden, sondern wie der Verwender sie möglichst unkompliziert für seine Bedürfnisse erweitern kann.

      Wie gestaltet man die Lösung so, dass sie nicht nur um weitere Grafikformate erweitert werden kann, sondern auch so, dass andere Typen berücksichtigt werden, die gegebenenfalls mit anderen Prüfroutinen getestet werden müssen?

      Ideal wäre vielleicht, dass WebP als Beispiel für die einfache Erweiterung weiterer Grafikformate verwendet wird, und vielleicht mit PDF zu zeigen, wie Prüfungen anderer Formate hinzugefügt werden können.

      dedlfix.

  3. problematische Seite

    Hallo Julius,

    Ich überarbeite gerade in meinem Benutzernamensraum (noch unvollständig, teilweise nur Stichpunkte) den Wiki-Artikel zum Thema Datei Upload mit PHP. Er ist unvollständig, zu umfangreich (drittlängster Artikel im gesamten Wiki!) und zu allgemein, um noch als Tutorial durchzugehen.

    Vielen Dank für dein Engagement. Ich finde es gut, richtig und wichtig, dass sich jemand eines solchen Themas annimmt. Ich habe ebenso wie @Matthias Scharwies keinerlei Bedenken, dass die Qualität des Artikels nicht unseren Ansprüchen genügen würde. Allerdings sollte es von vornherein nicht das Ziel sein, eine Kopiervorlage für alle denkbaren Anwendungsfälle bereitzustellen. Vielmehr sollten alle Probleme, die auftreten können, angesprochen und Lösungsvorschläge angeboten werden, was nicht heißt, dass es am Ende nicht doch ein fertiges Script geben kann.

    Die Idee, dass für GIF, PNG und JPG vorzustellen, finde ich ebenfalls charmant, wobei Gunnars Anmerkung bezüglich WebP nicht von der Hand zu weisen ist. Ein aktueller Artikel sollte auch aktuelle Entwicklungen berücksichtigen. Der Vorschlag, anhand WebP zu zeigen, wie man das Script auf andere Bildformate erweitern (und anhand PDF auf andere Dateitypen) kann ist im Sinne eines Tutorials goldrichtig.

    Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

    Auf jeden Fall. Und ich werde mich auch immer gegen eine Entfernung stemmen. Vielleicht könntest du ein wesentlich umfangreicheres / vollständiges Script bei GitHub hosten, so können Änderungswünsche per PR eingebracht werden und es könnte auf diese Weise, das, was den Rahmen eine Tutorials nun wirklich sprengen würde, trotzdem Bestandteil des Artikels sein. Ich meine wir haben sogar einen Account bei GitHub, ich weiß bloß grad nicht, wer die Zugangsdaten dazu hat.

    Bis demnächst
    Matthias

    --
    Du kannst das Projekt SELFHTML unterstützen,
    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
    1. problematische Seite

      Hallo Matthias,

      Allerdings sollte es von vornherein nicht das Ziel sein, eine Kopiervorlage für alle denkbaren Anwendungsfälle bereitzustellen. Vielmehr sollten alle Probleme, die auftreten können, angesprochen und Lösungsvorschläge angeboten werden, was nicht heißt, dass es am Ende nicht doch ein fertiges Script geben kann.

      Eine Kopiervorlage, die alle Eventualitäten berücksichtigt, wird auch nicht sonderlich leicht verständlich sein, von daher werde ich diese auch nicht anstreben. Der aktuelle Artikeltorso versucht ebenfalls alle Probleme zu lösen und löst dabei keins wirklich, wie mir scheint. Den Fehler bemühe ich mich, nicht zu wiederholen.

      Die Idee, dass für GIF, PNG und JPG vorzustellen, finde ich ebenfalls charmant, wobei Gunnars Anmerkung bezüglich WebP nicht von der Hand zu weisen ist. Ein aktueller Artikel sollte auch aktuelle Entwicklungen berücksichtigen. Der Vorschlag, anhand WebP zu zeigen, wie man das Script auf andere Bildformate erweitern (und anhand PDF auf andere Dateitypen) kann ist im Sinne eines Tutorials goldrichtig.

      Ist aufgenommen. WebP habe ich tatsächlich in seinen Features und Unterstützung unterschätzt, ich dachte, das würde niemand unterstützen, was nicht stimmt. PDF dürfte ein häufig verwendetes und aus Sicherheits-Sicht unkritisches Format sein, der MIME-Type ist gut zu erkennen und für idiotische PDF-Betrachter, die eingebette Skripte ausführen, kann die Anwendung nichts. Wie man den Upload um weitere Formate erweitert, werde ich in den Artikel einbauen. Die Risikobewertung und exemplarische Untersuchung einzelner Formate im Detail könnte in Zukunft auch in einem separaten Artikel kommen (siehe meine Antwort auf Felix’ Beitrag), das allerdings bei Gelegenheit später. Bei zu vielen Baustellen gleichzeitig wird in der Regel keine fertig.

      Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

      Auf jeden Fall. Und ich werde mich auch immer gegen eine Entfernung stemmen.

      (Nur dass das jetzt nicht so wirkt: Wenn ich wirklich mal etwas Unpassendes ins Wiki packen sollte und mir das jemand sauber darlegt, warum das so ist, dann werde ich mich sicher nicht gegen eine Änderung oder notfalls Entfernung stemmen.)

      Vielleicht könntest du ein wesentlich umfangreicheres / vollständiges Script bei GitHub hosten, so können Änderungswünsche per PR eingebracht werden und es könnte auf diese Weise, das, was den Rahmen eine Tutorials nun wirklich sprengen würde, trotzdem Bestandteil des Artikels sein. Ich meine wir haben sogar einen Account bei GitHub, ich weiß bloß grad nicht, wer die Zugangsdaten dazu hat.

      Das ist eine gute Idee! Wenn der Artikel mal fertig ist, wird dieses Beispielskript, das ein konkret definiertes Problem (sicherer Bilderupload und Bereitstellung) möglichst allgemein von A – Z löst, auch fertig sein. Ich komme darauf zurück. Aber bevor ich hier jemandem unnötigerweise Arbeit beschaffe, schreibe ich erst einmal Artikel und Skript.

      Gruß
      Julius

  4. problematische Seite

    Lieber Julius,

    Ich überarbeite gerade in meinem Benutzernamensraum (noch unvollständig, teilweise nur Stichpunkte) den Wiki-Artikel zum Thema Datei Upload mit PHP.

    vielen Dank für Dein Engagement! Auch ich hatte schon länger mit der Idee gespielt, diesen Artikel irgendwann einmal aufzusplitten, habe aber gerade ein viel zu großes Projekt an der Backe, um mich dem Wiki überhaupt zu widmen.

    • Man kann nur Bilder (JPEG, PNG und GIF) hochladen, diese werden auch mit mime_content_type überprüft (getimagesize bringt keinen Mehrwert, es wird explizit im Manual davon abgeraten, diese Funktion zur Überprüfung von Bildern zu verwenden).

    Wenn man die FileInfo-Erweiterung nicht hat, wäre damit aber ein besserer Schutz gegeben, als überhaupt keiner. Aber ich habe natürlich keine Ahnung, wer auf diese Erweiterung heute verzichten muss. Eignet sich getimagesize nicht wenigstens als Fallback?

    SVGs spare ich erst einmal aus, weil die zu riskant sind. Dafür gibt es validierende Libraries, nur erhöht das wieder die Komplexität des Ganzen.

    Warum nicht das Thema "Überprüfung auf gültige Bilddatei" in ein eigenes Unterkapitel auslagern? Dann kann man darauf verlinken und dort ausführlicher die Thematik des MIME-Sniffing behandeln. Denn wenn man schon externe Libraries benötigt, dann aber gleich richtig zuschlagen und zeigen, dass man das tatsächlich tun sollte und wie am besten! Wenn das dann ein weiteres Zusatzkapitel Composer bedeutet, dann ist das auch nicht grundverkehrt.

    • Dateinamen werden zufällig erzeugt, man spart sich die ganzen Probleme mit Case-sensitivity, Zeichen oder ganzen Dateinamen mit spezieller Bedeutung.

    Wie willst Du die Datenhaltung lösen, die den ursprünglich mitgelieferten Dateinamen mit dem zufällig erzeugten in Verbindung bringt?

    Mir ist bewusst, dass dies eine ähnliche Diskussion wie mit dem Loginsystem Anno 2016 ist. Nur halte ich das Risiko hier für besser beherrschbar und irgendwo muss man auch mal erklären, wie man einen Dateiupload grundsätzlich sicher umsetzt.

    Mach' Dir da mal keine Sorgen. Die Diskussion mit dem Login-Artikel haben wir letztlich dann doch noch in den Griff bekommen, nachdem wir hier ausreichend diskutiert haben. Du gehst genau den richtigen Weg, indem Du im Vorfeld schon die Debatte anstößt, um Konsens zu finden.

    Und nun zuletzt meine Frage: Meint ihr, dass dieser Artikel ins Wiki passen würde? Ich habe Bedenken, dass er am Ende wie das Loginsystem damals entfernt wird und die Arbeit letztlich für die Katz war.

    Nein, nix für die Katz, für das Wiki!

    Liebe Grüße

    Felix Riesterer

    1. problematische Seite

      Hallo Felix,

      Auch ich hatte schon länger mit der Idee gespielt, diesen Artikel irgendwann einmal aufzusplitten, habe aber gerade ein viel zu großes Projekt an der Backe, um mich dem Wiki überhaupt zu widmen.

      Dafür gibst du mir Rückmeldung zu meinen Überlegungen, vielen Dank dafür!

      • Man kann nur Bilder (JPEG, PNG und GIF) hochladen, diese werden auch mit mime_content_type überprüft (getimagesize bringt keinen Mehrwert, es wird explizit im Manual davon abgeraten, diese Funktion zur Überprüfung von Bildern zu verwenden).

      Wenn man die FileInfo-Erweiterung nicht hat, wäre damit aber ein besserer Schutz gegeben, als überhaupt keiner. Aber ich habe natürlich keine Ahnung, wer auf diese Erweiterung heute verzichten muss. Eignet sich getimagesize nicht wenigstens als Fallback?

      Ich denke nicht, die PHP-Entwickler schreiben explizit, dass getimagesize ein gültiges Bild erwartet, um zuverlässig sinnvolle Werte zu liefern: „This function expects filename to be a valid image file. If a non-image file is supplied, it may be incorrectly detected as an image and the function will return successfully, but the array may contain nonsensical values.“

      mime_content_type basiert (mittlerweile?) sogar auf FileInfo, nachdem es in der Doku eine Zeit lang als deprecated markiert war, es fehlt aber in keiner unterstützten PHP-Version, zumindest wenn man es nicht explizit deaktiviert. In der Doku wird es sogar als FileInfo zugehörig kategorisiert. Ich denke, dass ein Vorhandensein vorausgesetzt werden kann. Wenn nicht, funktioniert das Skript halt einfach nicht.

      SVGs spare ich erst einmal aus, weil die zu riskant sind. Dafür gibt es validierende Libraries, nur erhöht das wieder die Komplexität des Ganzen.

      Warum nicht das Thema "Überprüfung auf gültige Bilddatei" in ein eigenes Unterkapitel auslagern? Dann kann man darauf verlinken und dort ausführlicher die Thematik des MIME-Sniffing behandeln.

      Ich denke, dass das auf einer Datenbank mit Magischen Zahlen basierende mime_content_type erst einmal alles abdeckt, was man auf dem Gebiet Bilder brauchen könnte – schwierig würde es aber hingegen bei einem XML-basierten Format einer bestimmten Applikation, wenn man nicht text/xml, sondern application/vnd.scribus[1] erhalten möchte, muss man tätig werden und auf die Dateiendung oder die Dateistruktur selbst schauen. Ich denke, dass das für den Dateiupload mit seinen gut unterscheidbaren Dateitypen an sich erst einmal keine Rolle spielt, man aber die Komplexität des Themas an anderer Stelle im Wiki tatsächlich anreißen sollte und auch im entsprechenden Glossar-Eintrag darauf verweist.

      Das Thema ist dermaßen komplex und es gibt keine für alle Dateitypen anwendbares 1:1-Kochrezept (Audio/Video-Containerformate mit verschiedenen Codecs sind auch ein Thema für sich), das ist mir mittlerweile auch klar geworden, nachdem ich mich dumm und dämlich gesucht habe, was denn nun der Königsweg ist. Man kann höchstens als erste Instanz mime_content_type bemühen, muss die Fälle abfangen, in denen das nicht ausreicht und auf Dateiendung oder bestimmte Eigenschaften achten. Immer verbunden mit einer Risikoanalyse: Wenn diese Datei falsch eingestuft wird, welche Gefahr kann im gegebenen Kontext davon ausgehen?
      Weil das kompliziert ist, auch die Beschränkung auf Bilder.

      Ich glaube, das dürfte der Grundstein für einen eigenen Artikel zum Thema „MIME-Sniffing“ werden 🙃. Ich behalte es im Hinterkopf, die grundsätzlichen Überlegungen stehen hier ja schon.

      Denn wenn man schon externe Libraries benötigt, dann aber gleich richtig zuschlagen und zeigen, dass man das tatsächlich tun sollte und wie am besten! Wenn das dann ein weiteres Zusatzkapitel Composer bedeutet, dann ist das auch nicht grundverkehrt.

      Hehe, das gibt es sogar schon als eigenen Artikel 😀.

      • Dateinamen werden zufällig erzeugt, man spart sich die ganzen Probleme mit Case-sensitivity, Zeichen oder ganzen Dateinamen mit spezieller Bedeutung.

      Wie willst Du die Datenhaltung lösen, die den ursprünglich mitgelieferten Dateinamen mit dem zufällig erzeugten in Verbindung bringt?

      In meinem ursprünglichen Ansatz gar nicht, hier im Forum wird er ja auch nicht gespeichert oder zumindest nicht verwendet, deshalb kann es je nach Anwendungsfall entfallen. Es ist bisher lediglich als mögliche Erweiterung vorgeschlagen (ohne vorher groß darüber nachzudenken, habe ich öfter einen Abschnitt mit möglichen Erweiterungen und Hinweisen dazu in meinen Tutorials integriert, ich halte das für didaktisch sinnvoll). Zum Thema Datenspeicherung habe ich bereits etwas geschrieben (vor ein paar Jahren kamen mal viele Fragen, wie man denn Daten einfach und persistent speichern kann und welche Vor- und Nachteile eine Lösung jeweils hat) und den Artikel Datenbank mit PHP habe ich auch auf der Agenda. Die drei Artikel können dann jeweils aufeinander verweisen.

      Aktuell tendiere ich auch dazu, kein vollständiges Skript im Wiki vorzustellen, weil es eh jeder für sich anpassen muss und es auf GitHub bei SELFHTML auch besser aufgehoben wäre (Danke @Matthias Apsel).

      Gruß
      Julius


      1. $ file -bi ScribusDokument.sla liefert text/xml; charset=us-ascii, was ungenau, aber nicht gänzlich falsch ist, da es ein XML-Dokument ohne über ASCII hinausgehende Zeichen ist. ↩︎

  5. problematische Seite

    Meinen Segen hast du, ich werde nach dem letzten Eklat grundsätzlich nichts mehr aus dem Wiki löschen. Nach dem Thread hier habe ich aber auch den Eindruck, dass du dafür ausreichend qualifiziert bist und dich mit der Thematik auskennst. Ich find's auch gut, dass dein Template schon Links auf die OWASP-Empfehlungen enthält. Ich hab den Thread mal abonniert und gebe gerne Feedback, wenn sich etwas tut.

    1. problematische Seite

      Hallo 1unitedpower,

      Meinen Segen hast du, ich werde nach dem letzten Eklat grundsätzlich nichts mehr aus dem Wiki löschen.

      (Nur so als Notiz, es war nicht meine Intention, dein Eingreifen damals nicht bewerten. Ich halte es aber ebenfalls für richtig, komplexe, sicherheitsrelevante Software nicht über ein Wiki zu pflegen.)

      Ich hab den Thread mal abonniert und gebe gerne Feedback, wenn sich etwas tut.

      Danke!

      Gruß
      Julius

  6. problematische Seite

    Hallo Julius,

    bevor der Thread hier zu früh im Archiv landet, teile ich lieber schnell noch einen Fund: WordPress (hier die Stelle im Code) schaut erst anhand der Dateiendung und des vom Browser mitgeteilten MIME-Types, ob es sich um ein Bild handeln soll und jagt erst dann ggf. getimagesize() in der Funktion wp_get_image_mime() auf das Bild los.

    Gruß
    Julius

    1. problematische Seite

      Hello,

      bevor der Thread hier zu früh im Archiv landet, teile ich lieber schnell noch einen Fund: WordPress (hier die Stelle im Code) schaut erst anhand der Dateiendung und des vom Browser mitgeteilten MIME-Types, ob es sich um ein Bild handeln soll und jagt erst dann ggf. getimagesize() in der Funktion wp_get_image_mime() auf das Bild los.

      ** puh **

      um zu durchschauen, was die tatsächlich treiben, muss man aber ziemlich tief in die Funktionsverschachtelungen eintauchen. Hast Du das mal versucht?

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. problematische Seite

        Hallo TS,

        um zu durchschauen, was die tatsächlich treiben, muss man aber ziemlich tief in die Funktionsverschachtelungen eintauchen. Hast Du das mal versucht?

        Ja, habe ich, ich werde aber noch einen zweiten Blick drauf werfen müssen, nicht, dass ich mich vertan habe. Der Code ist jedenfalls gut kommentiert. Soweit ich das verstehe, schauen die erst auf den übermittelten Dateinamen und MIME-Type und schauen, dann mit Fileinfo und getimagesize und Konsorten, ob das auch passt.

        Gruß
        Julius

        1. problematische Seite

          Hello,

          um zu durchschauen, was die tatsächlich treiben, muss man aber ziemlich tief in die Funktionsverschachtelungen eintauchen. Hast Du das mal versucht?

          Ja, habe ich, ich werde aber noch einen zweiten Blick drauf werfen müssen, nicht, dass ich mich vertan habe. Der Code ist jedenfalls gut kommentiert. Soweit ich das verstehe, schauen die erst auf den übermittelten Dateinamen und MIME-Type und schauen, dann mit Fileinfo und getimagesize und Konsorten, ob das auch passt.

          Schauen die auf den übermittelten MIME-Type oder bestimmen sie ihn auf dem Server selber? Könntest Du bitte den Link zu der Stelle auch nochmal posten?

          Glück Auf
          Tom vom Berg

          --
          Es gibt nichts Gutes, außer man tut es!
          Das Leben selbst ist der Sinn.
          1. problematische Seite

            Hallo,

            schrieb Julius doch

            Gruß
            Kalk

            1. problematische Seite

              Hello,

              schrieb Julius doch

              kann ich leider nicht erkennen, dass das tief genug geschaut ist.
              Ich lande immer bei apply_filters() und komme da nicht weiter.

              getimagesize() funktioniert (?) jedenfalls nur für Images.

              Die neuen MIME-Funktionen von PHP werden mEn gar nicht benutzt.

              Gibt es eine Möglichkeit, die Funktionshierarchie zu tracken, also den Nutzingsbaum von einer Stelle der Doku aus aufzubauen?

              Glück Auf
              Tom vom Berg

              --
              Es gibt nichts Gutes, außer man tut es!
              Das Leben selbst ist der Sinn.
              1. problematische Seite

                Die neuen MIME-Funktionen von PHP werden mEn gar nicht benutzt.

                Ich bin mir jetzt nicht ganz sicher, was Du mit „Die neuen MIME-Funktionen“ genau meinst, aber in dem von Julius verlinkten Skript finde ich:

                2865: $finfo     = finfo_open( FILEINFO_MIME_TYPE );
                

                Das wäre ein Kandidat und wird auch benutzt.

                https://www.php.net/manual/de/fileinfo.constants.php „sagt“:

                FILEINFO_MIME_TYPE (integer) Gibt den MIME-Typ zurück. Verfügbar seit PHP 5.3.0.

                https://www.php.net/manual/de/intro.fileinfo.php „sagt“:

                Einführung:

                Die Funktionen in diesem Modul versuchen den Typ und die Codierung des Inhaltes einer Datei durch untersuchen bestimmter magischen Byte-Sequenzen an spezifischen Stellen innerhalb der Datei. Obwohl dies kein "kugelsicherer" Ansatz ist machen sie dennoch gute Arbeit.

                https://www.php.net/manual/de/function.mime-content-type wiederum weist mime_content_type() als Bestandteil der fileinfo-Funktions-Gruppe aus. Sagt aber auch, dass gäbe es schon seit PHP 4.3.0:

                (PHP 4 >= 4.3.0, PHP 5, PHP 7)

                Das macht mich nun etwas unsicher, denn eine der neuen MIME-Funktionen“ kann es demnach nicht sein…

                Oder hast Du was anderes gemeint?

                Anbei: An dem gezeigten Skript gäbe einiges zu kritisieren. Z.B. die Vermischung von Programm und Daten…

                1. problematische Seite

                  Hallo raketenquelltextleser,

                  Anbei: An dem gezeigten Skript gäbe einiges zu kritisieren. Z.B. die Vermischung von Programm und Daten…

                  Beziehst du dich auf die Auflistungen der MIME-Types und Dateiendungen im Code? So ad hoc sehe ich kein Problem darin, schließlich sind die durchgeführten Prüfungen ja auch eng mit den jeweiligen Dateiformaten verbunden.

                  Mir fällt aus aktuellem Anlass ein anderes, unschöneres Beispiel ein: WoltLab (hab ich mir nicht ausgesucht) hat im WCF eine hartkodierte Auswahl von 100 der über 400 von der IANA definierten Zeitzonen im Code, statt die sich aus der zuständigen PHP-API zu ziehen. Standards ohne Not nicht vollständig zu implementieren und das scheinbar abgesehen vom Code nicht zumindest zu dokumentieren, sorgt immer wieder für unerfreuliche Überraschungen.

                  Gruß
                  Julius

              2. problematische Seite

                Hallo TS,

                schrieb Julius doch

                kann ich leider nicht erkennen, dass das tief genug geschaut ist.
                Ich lande immer bei apply_filters() und komme da nicht weiter.

                apply_filter() ist die Möglichkeit, für anderen Code in bestimmte Aktionen einzugreifen. Je nachdem, wo es sich befindet, ist für das Verständnis nicht weiter erforderlich, ganz nachzuvollziehen, was genau da für Filter angewendet werden.

                getimagesize() funktioniert (?) jedenfalls nur für Images.

                Es wird ja auch nur auf Bilder angewendet, wenn diese aufgrund anderer Kriterien wahrscheinlich ein Bild sind. Das ist schon was anderes, ob man damit eine beliebige Datei daraufhin überprüft, ob es sich um ein Bild handelt.

                Gruß
                Julius