Peter: Aufrufschutz mit .htaccess und PHP

Hallo, ich brauche doch mal eure Hilfe… In einem Ordner sind PDF-Dateien abgelegt die bislang direkt, das heisst per Link auf die Datei im Browser angezeigt werden. Jetzt muss aber dieser Aufruf kontrolliert werden, d.h. das technisch gesehen zwar der Link nach wie vor genutzt werden soll, intern aber dann per htaccess eine PHP-Datei angesprochen werden soll die den zulässigen Zugriff prüft und dann bei positiver Prüfung die Datei ausliefert bzw. bei negativem Ergebnis zu einer Login-Seite leiten soll. Ich habe die htaccess wie folgt aufgebaut:

RewriteEngine on
RewriteRule (.*)\.pdf$ https://pfad_zur_pruefdatei.php?dt=$1

Diese scheint auch so zu funktionieren. Die PHP-Datei sind (gekürzt) so aus:

<?php
session_start();
if(is_numeric($_SESSION['uid']) && $_SESSION['uid']>0) { # Benutzer eingeloggt
	$uid = $_SESSION['uid'];
	if(isset($_GET['dt']) && $_GET['dt']!='') { # Aufruf via Direkturl auf die PDF (alte Links)
		$datei = $_GET['dt'];
	  header('Content-Type: application/pdf');
		readfile("absoluter_pfad_zur_pdf/".$datei);  
	}
} else { # Kein aktiver Login
	header("Location:loginseite.php");
}
?>

Mein Problem ist nun, das die PHP angesprochen wird, aber die PDF eine 0-byte-Datei ist, d.h. keine Ausgabe. Und das ist mein Problem, ich kann nicht nachvollziehen, warum dort nichts ankommt.

VG Peter

  1. RewriteEngine on
    RewriteRule (.*)\.pdf$ https://pfad_zur_pruefdatei.php?dt=$1`
    

    Die PHP-Datei sind (gekürzt) so aus:

    <?php
    session_start();
    if(is_numeric($_SESSION['uid']) && $_SESSION['uid']>0) { # Benutzer eingeloggt
    	$uid = $_SESSION['uid'];
    	if(isset($_GET['dt']) && $_GET['dt']!='') { # Aufruf via Direkturl auf die PDF (alte Links)
     		$datei = $_GET['dt'];
     	  header('Content-Type: application/pdf');
     		readfile("absoluter_pfad_zur_pdf/".$datei);  
     	}
    } else { # Kein aktiver Login
    	header("Location:loginseite.php");
    }
    ?>
    

    Mein Problem ist nun, das die PHP angesprochen wird, aber die PDF eine 0-byte-Datei ist, d.h. keine Ausgabe.

    Vermutlich ist an readfile("absoluter_pfad_zur_pdf/".$datei); etwas falsch. Da es der Befehl nicht ist, bleibt der Parameter. Es kann aber ebenso sein, dass der Webserver die Datei nicht lesen darf. (chmod 644 <DATEI> oder chmod a+r <DATEI>) könnte helfen. Das Verzeichnis mit dem PDF muss vom Webserver betreten und gelesen werden können. (chmod 755 <DIR> oder chmod a+rw <DIR>)

    Das Error-Log sollte Dir mehr sagen. Ebenso kannst Du das Error-Reporting anschalten:

    error_reporting(E_ALL);
    ini_set("display_errors", 1);
    

    und mit den Entwicklertools Deines Browsers den Quelltext der Antwort ansehen.

    Warnungen:

    1.)

    Außerdem solltest Du auf das ?> am Ende verzichten, weil sonst womöglich nicht nur "hyperliquide" sondern unwillkommene Leerzeichen und Zeilenumbrüche gesendet werden. Wie die verschiedenen "PDF-Anzeiger" damit umgehen ist unklar, das kann zu Fehlermeldungen statt zur Anzeige führen.

    2.)

    Ich sehe nichts, was verhindert, das ggf. Dateien abgeholt werden, die in anderen als dem gewünschtem Verzeichnis liegen. Das ist gefährlich!

    Hinweise:

    Ich würde noch folgende Header senden:

    header( 'Content-Disposition: attachment; filename=' . $filename ); 
    header( 'Content-Transfer-Encoding: binary' ); 
    header( 'Content-Length: ' . filesize( $file ) ); 
    
    1. Hinweise: (Fortsetzung)

      Im Falle, dass die Datei nicht existiert (prüfen mit file_exists();) oder der Zugriff nicht erlaubt ist (prüfen mit is_readable();), solltest Du

      header( 'HTTP/1.0 404 Not Found' );
      echo '<html><h1>Not Not Found</h1></html>';
      

      senden.

      Natürlich kannst das auch unterscheiden und, falls nicht lesbar, mit einem 403er ("Forbidden") reagieren. aber dann lieferst Du einem Angreifer schon wieder zu viele Informationen. Der erfährt dann nämlich, dass eine Datei mit einem bestimmten Name überhaupt vorhanden ist. Das ist regelmäßig höchst unklug.

      Korrektur:

      Falsch:

      Das Verzeichnis mit dem PDF muss vom Webserver betreten und gelesen werden können. (chmod 755 <DIR> oder chmod a+rw <DIR>)

      Richtig:

      Das Verzeichnis mit dem PDF muss vom Webserver betreten und gelesen werden können. (chmod 755 <DIR> oder chmod a+rx <DIR>)

      1. Hello,

        Im Falle, dass die Datei nicht existiert (prüfen mit file_exists();) oder der Zugriff nicht erlaubt ist (prüfen mit is_readable();), solltest Du

        Diese Funktionen sollten für den nachfolgenden Zugriff auf die Dateien NICHT benutzt werden, sondern nur für zeitlich abgekoppelte Übersichten (Listen). Warum sollte man ein TOCTTOU-Problem erzeugen, wenn es doch eine fopen()-Funktion mit aussagefähiger Fehleranalyse gibt?

        Den Error muss man mWn zwar leider immer noch textlich auswerten. Es gibt also mWn noch keine eindeutige Fehlernummer (bitte mich zu korrigieren, wenn das in PHP 7.x nachgebessert wurde), aber das ist trotzdem qualifiziert möglich, da die Texte immer derselben Regel folgen.

        Die Verwendung der namensbasierten Funktionen (also NICHT mit durchgängigem Handle) ist nur dann mäßig sinnvoll, wenn es nur um reine Anzeigefunktionen ohne nachfolgende Bearbeitung geht.

        Liebe Grüße
        Tom S.

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