Blog von MuLu

18. April 2017

mod_xsendfile mit Symlinks emulieren

Filed under: IT — Schlagwörter: , , , , , — Lukas @ 21:27

Moin zusammen,

heute mal wieder ein Blogpost mit hoffentlich praktischem Nutzen.
Jeder, der ein bisschen in der Webentwicklung tätig war, wird früher oder später auf ein Problem stoßen, wenn er große Dateien einer bestimmten Nutzergruppe zur Verfügung stellen muss. Da gibt es mehrere Möglichkeiten mit mehr oder weniger großen Nachteilen.
– Per Scriptsprache ausliefern: Kann man machen, aber irgendwann greift ggf. ein eingerichtetes Laufzeitlimit. Für größere Dateien also ungeeignet.
– Dateipfad kryptisch benennen: Naja, Linkweitergabe, auch nicht sonderlich toll.
– Per Webserver-Modul ausliefern: Damit wären wir beim Thema mod_xsendfile. Man sagt dem Modul mit einem Header welche Datei ausgeliefert werden soll. Vorher kann man dann die Rechte prüfen, Downloads zählen, … Nachteil: Das Modul muss installiert sein.
Will der Hoster das Modul nicht installieren ist das natürlich suboptimal. Da das bei mir der Fall war hatte ich vor einigen Jahren mal eine Lösung mit temporären Symlinks entwickelt. Die funktioniert zwar, aber war recht schnell – Entschuldigung – hingerotzt.
Da auch andere Leute an der Lösung interessiert waren, habe ich mich noch mal ein Wochenende hingesetzt und das als PHP-Klasse und WordPress-Plugin neu angefangen. Das Ergebnis und Nutzung stelle ich in diesem Post mal vor.
tl;dr: Die Klasse Plugin findet ihr aktuell nur in einem Github-Repo: https://github.com/muellerlukas/simulatesendfile
Oder auch direkt als WordPress-Plugin: https://github.com/muellerlukas/simulatesendfile_wordpress

Die generelle Funktionsweise von mod_xsendfile ist in etwa so:
1) Der User will eine Datei runterladen und ruft einen Link auf.
2) Das entsprechende Script prüft ggf. die Berechtigung des Downloads, zählt den Download, …
3) Wenn das Script die Datei aufruft schickt es einen Header (je nach Webserver „X-Sendfile“, „X-Accel-Redirect“ oder „X-Lighttpd-Sendfile“) mit dem Pfad zur Datei und beendet die Verarbeitung.
4) Das Modul fängt diesen Header ab und liefert die Datei selbst aus.
5) Somit spielt die Scriptlaufzeit keine Rolle mehr und auch große Dateien können ausgeliefert werden.
Das ist natürlich schön weil man so seine üblichen Prüfroutinen beibehalten kann.

Da mein Hoster dieses Modul nicht installieren wollte, ich aber von der Scriptlaufzeit unabhängig sein wollte, wurde also eine Lösung (zumindest für PHP-Systeme) entwickelt: Die ganz grobe Funktionsweise versuche ich mal zu erläutern.
1) Eine Methode wird als shutdown-Funktion bei PHP registriert, rennt also sobald das Script beendet wird – und damit kurz bevor die Kontrolle an den Webserver zurück geht.
2) Sollte keiner der Header vorhanden sein, dann gibt es nichts zu tun.
3) Ansonsten wird für die Datei ein Ordner mit Zufallswert + Symlink zu der Datei angelegt. Warum nicht direkt ein Symlink mit Zufallsnamen? Easy: So kriegt der Nutzer auch den Dateinamen direkt vorgegeben.
4) Die entsprechenden Header werden nun entfernt und stattdessen eine Weiterleitung zum Symlink vorgenommen. Die Kontrolle ist wieder beim Webserver und der Nutzer kann runterladen.
5) Zusätzlich gibt es einen Garbage-Collector der Symlinks + Ordner älter als 1 Stunde (standardmäßig, änderbar) löscht und somit weiteren Download verhindert

Leider gibt es Software (wie z.B. WooCommerce) mit Prüfung ob das Modul im Apache aktiviert ist. Ist das nicht der Fall wird weiterhin über PHP selbst aufgeliefert. Bei jedem Update patchen war mir zu blöd, also hat sich dafür dann auch ein ziemlich dreckiger Hack entwickelt. Eine Datei die ins Template eingebunden werden muss („dirtyhacks.php“) erstellt die Funktion apache_get_modules() wenn sie nicht existiert und behauptet einfach: „mod_xsendfile ist installiert.“
Das funktioniert so lange wie PHP nicht selbst als Modul installiert ist (was bei den meisten Sharehostern auch nicht der Fall ist). Als Modul gibt es die Funktion und kann somit nicht einfach überschrieben werden. (Okay, mit einer PECL-Extension. Aber wenn ich die installieren kann, dann kann ich auch das Modul installieren.)

Das war’s eigentlich an sich auch schon. Im Repo ist auch eine kurze Readme enthalten mit einem kleinen Beispiel zur Verwendung.
Dazu muss ich allerdings noch sagen: Sowohl die Klasse udn auch das WordPress-Plugin sind aktuell noch in einem sehr frühen Stadium. So gab es noch keine genauen Tests mit Windows-Systemen (ja, da gibt es wieder Unterschiede bei den Symlinks), PHP <7 und nginx & Lighty.

Keine Kommentare »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

*

Powered by WordPress