Skip to content

media/{filename}/update (PUT/PATCH): Multipart-File-Upload schlägt bei aktivem enable_post_data_reading fehl #81

Description

@skerbis

Problem

handleUpdateMedia() erwartet bei multipart/form-data-Requests, dass parseMultipartInput() den Body manuell aus php://input parsen kann, weil PHP $_FILES nur bei POST automatisch befüllt (Kommentar im Code: "PUT/PATCH füllt $_FILES nicht").

Das stimmt nur zur Hälfte: Ist enable_post_data_reading aktiv (php.ini-Default, On), liest PHP den kompletten multipart/form-data-Body bereits vor jedem Nutzcode aus php://input, um ihn (nur bei POST) in $_FILES zu befüllen. Bei PUT/PATCH wird der Body dabei trotzdem vollständig konsumiert und verworfen, ohne $_FILES zu befüllen. Für parseMultipartInput() bleibt dann buchstäblich nichts mehr übrig — file_get_contents('php://input') liefert einen leeren String, obwohl CONTENT_LENGTH einen echten Body ankündigt.

Der Request schlägt dabei nicht sichtbar fehl: handleUpdateMedia() fällt in diesem Fall auf reine Metadaten zurück (Titel/Kategorie unverändert), rex_media_service::updateMedia() läuft trotzdem "erfolgreich" durch (bumpt nur updatedate), die Antwort ist ein normales 200 {"message":"Media updated",...}. Der Client bekommt also Erfolg gemeldet, die Datei wird aber nie ersetzt.

Reproduktion

  1. enable_post_data_reading = On (Standard).
  2. Multipart-Request mit method: PATCH (oder PUT) und einem file-Feld an media/{filename}/update senden (z.B. per fetch() mit FormData+method:'PATCH').
  3. Response ist 200 mit Erfolgsmeldung.
  4. Die Mediendatei auf dem Server ist unverändert (Dateigröße/Breite/Höhe/Inhalt identisch zu vorher), nur updatedate hat sich geändert.

Direkt verifiziert per Debug-Logging in parseMultipartInput():

AT HANDLE() START rawlen=0 CONTENT_LENGTH=2723 method_raw=PATCH FILES=[] POST=[]
boundary='----WebKitFormBoundaryXXX' rawlen=0

CONTENT_LENGTH zeigt den echten Body an, php://input ist aber bereits leer, noch bevor RouteCollection::handle() überhaupt mit dem eigentlichen Routing beginnt.

Mögliche Fixes

  • In handleUpdateMedia() vor dem Aufruf von parseMultipartInput() prüfen, ob der Request tatsächlich als POST hereinkam (z.B. über einen X-HTTP-Method-Override: PATCH-Header geroutet) und in dem Fall $_FILES/$_POST bevorzugen, die PHP dann bereits korrekt nativ befüllt hat.
  • Und/oder: In der Doku/im Code-Kommentar explizit darauf hinweisen, dass ein Client die Datei nur zuverlässig per echtem PUT/PATCH übertragen kann, wenn enable_post_data_reading = Off gesetzt ist — das ist aber eine globale, invasive Einstellung (macht $_POST/$_FILES für die GESAMTE REDAXO-Installation leer, betrifft z.B. auch den Login), in der Praxis für die meisten Installationen keine Option.
  • Alternativ die Route zusätzlich für POST öffnen (mit einem separaten Unterscheidungsmerkmal, z.B. einem Query-Parameter oder eigenen Scope), damit Clients native POST-Uploads nutzen können, ohne den PUT/PATCH-Semantikbruch zu riskieren.

Umgebung

  • PHP 8.4.24, Apache/mod_php, Debian-Docker-Image (docker-library/php)
  • enable_post_data_reading => On => On (php.ini-Default, nicht projektspezifisch verändert)

Ich habe das für ein abhängiges Addon (MediaPlace) vorerst über einen eigenen, addon-internen Endpunkt umgangen (echtes POST an eine eigene Route, ruft serverseitig direkt rex_media_service::updateMedia()), wollte den eigentlichen Bug hier aber melden, da er wahrscheinlich jede Installation mit PHP-Default-Einstellungen betrifft, die versucht, media/{filename}/update mit einer neuen Datei aufzurufen.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions