it-swarm.com.de

Herunterladen einer Datei von einem externen Webstamm

Ich habe Code zum Herunterladen von Zip-Dateien eingerichtet, die sich in einem Ordner oberhalb des Webstamms befinden. Der Download wird von einer Benutzerkontoseite in WordPress ausgelöst. Sie müssen nicht auf Bankebene gesichert sein, aber ich möchte den direkten Dateizugriff von außerhalb der Site verhindern und sie nur für Benutzer mit den richtigen Berechtigungsstufen und nur auf der Kontoseite des entsprechenden Benutzers zugänglich machen. Die Seite ist komplett https.

Der Ordner, in dem sich die Zip-Dateien befinden, ist über htaccess geschützt.

Jedem Benutzer, der einer bestimmten Benutzerrolle zugewiesen ist, wird auf der Seite "Konto" ein Download-Link angezeigt:

if(current_user_can('download_these_files')){
    $SESSION['check'] = 'HVUKfb0IG1HIzHxJj5fZ';
    ?>
        <form class="user-file-download-form" action="/download.php" method="POST">
            <input type="submit" name="submit" value="Download File">
        </form>
    <?php
}

Dieses Formular wird an download.php gesendet, das sich im Webstamm befindet und Code enthält, den ich mit Hilfe von Google zusammengestellt habe.

session_start();
if( isset( $_POST['submit'] ) ){
    $check = $_SESSION['check'];
    if( $check === 'HVUKfb0IG1HIzHxJj5fZ' ){
        $file = /path/to/file/above/root.Zip;
        header('Content-Description: File Transfer');
        header('Content-Type: application/Zip');
        header('Content-Disposition: attachment; filename=' . basename($file));
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        ob_clean();
        flush();
        readfile( $file );
        exit;
    }else{
        header( 'Location: https://example.com/404page/' );
}else{
    header( 'Location: https://example.com/404page/' );
}

Das funktioniert einwandfrei. Aber ich kann nicht anders als mich zu fragen, ob ich etwas anderes machen sollte. Ich hatte gehofft, Informationen darüber zu erhalten, ob diese Implementierung produktionsbereit ist oder ob ich etwas Wichtiges vermisse, da dies das erste Mal ist, dass ich so etwas versuche.

Vielen Dank.

1
Rob

Was Sie dort haben, ist produktionsbereit. Es gibt jedoch Raum für kleinere Verbesserungen, daher werde ich Sie darauf hinweisen. Siehe auch meine Anmerkungen zu X-Sendfile und X-Accel-Redirect.


Ersetzen Sie diese Zeilen:

ob_clean();
flush();

mit den folgenden:

while (@ob_end_clean());

Wenn sich bereits etwas im Ausgabepuffer befindet, möchten Sie es nicht löschen, sondern nur bereinigen. Wenn Sie es leeren, wird dem Inhalt Ihrer herunterladbaren Datei das Präfix vorangestellt Inhalt des Ausgabepuffers, der nur dazu dient, die herunterladbare Datei zu beschädigen. Siehe: http://php.net/manual/en/function.ob-end-clean.php


Nach dieser Zeile:

$file = /path/to/file/above/root.Zip;

Fügen Sie Folgendes hinzu, um sicherzustellen, dass die GZIP-Komprimierung auf Serverebene deaktiviert ist. Dies hat möglicherweise keinen Einfluss auf Ihren aktuellen Webhost. Verschieben Sie die Site jedoch an einen anderen Ort. Ohne diese Zeilen kann das Skript möglicherweise fehlerhaft funktionieren.

@ini_set('zlib.output_compression', 0);
if(function_exists('Apache_setenv')) @Apache_setenv('no-gzip', '1');
header('Content-Encoding: none');

Achtung: Seien Sie vorsichtig bei der Verwendung dieser PHP-gesteuerten Datei-Download-Technik für größere Dateien (z. B. über 20 MB). Warum? Zwei Gründe:

  • PHP hat ein internes Speicherlimit. Wenn readfile() diese Grenze überschreitet, wenn die Datei in den Speicher eingelesen und einem Besucher zugestellt wird, schlägt Ihr Skript fehl.

  • Zusätzlich haben PHP Skripte ein Zeitlimit. Wenn ein Besucher einer sehr langsamen Verbindung lange braucht, um eine größere Datei herunterzuladen, tritt eine Zeitüberschreitung des Skripts auf und der Benutzer kann einen fehlgeschlagenen Download-Versuch ausführen oder eine teilweise/beschädigte Datei erhalten.


Achtung: Beachten Sie auch, dass PHP-gesteuerte Dateidownloads mit der Technik readfile() keine wiederaufsetzbaren Bytebereiche unterstützen. Wenn Sie den Download pausieren oder in irgendeiner Weise unterbrechen, haben Sie keine Möglichkeit, den Download fortzusetzen. Sie müssen den Download erneut starten. Es ist möglich, Bereichsanforderungen (Resume) in PHP zu unterstützen, aber das ist mühsam .


Langfristig ist mein Vorschlag, dass Sie nach einer viel effektiveren Möglichkeit suchen, geschützte Dateien bereitzustellen, die als X-Sendfile in Apache und X-Accel-Redirect in Nginx bezeichnet werden.

X-Sendfile und X-Accel-Redirect arbeiten beide mit demselben zugrunde liegenden Konzept. Anstatt eine Skriptsprache wie PHP zum Abrufen einer Datei in den Speicher aufzufordern, weisen Sie den Webserver einfach an, eine interne Umleitung durchzuführen und den Inhalt einer ansonsten geschützten Datei bereitzustellen. Kurz gesagt, Sie können einen Großteil der oben genannten Probleme beseitigen und die Lösung auf header('X-Accel-Redirect: ...') reduzieren.

2
jaswrks