Wolfgang: htaccess: Freigabe aus einem bestimmten Ordner

Schönen guten Morgen,

diesmal eine Frage bzgl. einer htaccess Datei.
Meine Verzeichnisstruktur sieht unter anderem so aus:

-puplic_html
  - intern
    - dataintern
    - toolsintern

Die Dateien im Ordner "intern" werden alle über php geschützt und lassen sich nur nach einem Login öffnen.
Nun möchte ich, dass diejenigen, die bereits Zugang in der internen Ordner haben (also eingeloggt sind) auch die Dateien aus "dataintern" oder "toolsintern" öffnen bzw. downloaden können.
Wenn man nicht eingeloggt ist, soll dies nicht möglich sein.

Würde mich sehr über konstruktive Vorschläge oder Lösungen freuen!

Vielen Dank und liebe Grüße,
Wolfgang

  1. Hallo,

    diesmal eine Frage bzgl. einer htaccess Datei.
    Meine Verzeichnisstruktur sieht unter anderem so aus:

    -puplic_html
      - intern
        - dataintern
        - toolsintern

    wirklich "public" mal mit 'p' in der Mitte? Eigenwillig ...

    Die Dateien im Ordner "intern" werden alle über php geschützt und lassen sich nur nach einem Login öffnen.
    Nun möchte ich, dass diejenigen, die bereits Zugang in der internen Ordner haben (also eingeloggt sind) auch die Dateien aus "dataintern" oder "toolsintern" öffnen bzw. downloaden können.
    Wenn man nicht eingeloggt ist, soll dies nicht möglich sein.

    Ich weiß nicht, wie dein Login-System arbeitet - aber normalerweise würde ich erwarten, dass der Zugriff auf untergeordnete Verzeichnisse automatisch verhindert ist, wenn ich schon auf das Elternverzeichnis nicht zugreifen kann. Alles andere wäre ... hmm, zumindest sehr ungewöhnlich.

    Würde mich sehr über konstruktive Vorschläge oder Lösungen freuen!

    Würde mich sehr über etwas genauere Hinweise zu deinen gegebenen Voraussetzungen freuen. Ach so, und wo kommt nun die eingangs erwähnte .htaccess ins Spiel?

    Ciao,
     Martin

    --
    Drei Sachen vergesse ich immer wieder: Telefonnummern, Geburtstage und ... äääh ...
    1. Hey Martin,

      wirklich "public" mal mit 'p' in der Mitte? Eigenwillig ...

      Das war soweit ein Schreibfehler meinerseits. Der Ordner heißt public...

      Ich weiß nicht, wie dein Login-System arbeitet - aber normalerweise würde ich erwarten, dass der Zugriff auf untergeordnete Verzeichnisse automatisch verhindert ist, wenn ich schon auf das Elternverzeichnis nicht zugreifen kann. Alles andere wäre ... hmm, zumindest sehr ungewöhnlich.

      Mein Login-System orientiert sich größtenteils an diesem Tutorial:
      http://www.yubb.de/artikel523.html

      Würde mich sehr über etwas genauere Hinweise zu deinen gegebenen Voraussetzungen freuen. Ach so, und wo kommt nun die eingangs erwähnte .htaccess ins Spiel?

      Über den Browser kann man die einzelnen Ordner nicht anschauen, wenn man nicht eingeloggt ist.
      Kennt man allerdings den genauen Pfad z.B. zu "intern/dataintern/file.zip", dann kann man diese auch ohne Login downloaden.
      Genau das wollte ich mit einer htaccess (die noch nicht existiert) unterbinden, dass man die Datei nur Downloaden kann, wenn man sie bereits aus dem Verzeichnis "intern" aufruft.

      Hoffe, das hilft mal weiter...

      Vielen Dank schonmal!

      1. Hallo Wolfgang,

        »» Ich weiß nicht, wie dein Login-System arbeitet [...]
        Mein Login-System orientiert sich größtenteils an diesem Tutorial:
        http://www.yubb.de/artikel523.html

        oh, verstehe. Das beschränkt also nur den Zugriff auf PHP-Scripte, und auch nur auf die, die das ausdrücklich so "wollen". Das ist natürlich nicht schön.

        Über den Browser kann man die einzelnen Ordner nicht anschauen, wenn man nicht eingeloggt ist.
        Kennt man allerdings den genauen Pfad z.B. zu "intern/dataintern/file.zip", dann kann man diese auch ohne Login downloaden.

        Ja, das ergibt sich zwangsläufig aus der beschriebenen Konstruktion.

        Genau das wollte ich mit einer htaccess (die noch nicht existiert) unterbinden, dass man die Datei nur Downloaden kann, wenn man sie bereits aus dem Verzeichnis "intern" aufruft.

        Dann ist es wohl die einfachste (und auch die sauberste) Methode, den Zugang komplett auf HTTP-AUTH umzustellen und das bisherige Login-System, das IMHO mehr Schein als Sein ist, zu entsorgen.
        Einziger Schönheitsfehler: Zur Eingabe der Zugangsdaten poppt eben der browsereigene User/Passwort-Dialog auf und nicht ein selbst gestyltes Formular. Das halte ich aber für akzeptabel.

        Eine Alternative wäre, die Dateien in ein Verzeichnis zu legen, das per HTTP nicht zugänglich ist (außerhalb des Domain Root oder mit Deny From All zugemacht), und den Download ebenfalls über ein PHP-Script zu regeln. Die Downloads hätten dann halt URLs wie etwa /download.php?file=foo.zip oder ähnlich.

        So long,
         Martin

        --
        Wenn zwei dasselbe tun, sind sie vielleicht bald zu dritt.
        1. Hey Martin

          Eine Alternative wäre, die Dateien in ein Verzeichnis zu legen, das per HTTP nicht zugänglich ist (außerhalb des Domain Root oder mit Deny From All zugemacht), und den Download ebenfalls über ein PHP-Script zu regeln. Die Downloads hätten dann halt URLs wie etwa /download.php?file=foo.zip oder ähnlich.

          Genau auf diese Alternative wollte ich hinaus. Ich hab auf verschiedenen Seite gelesen, dass man das über Direktiven, wie <Files>, <FilesMatch> oder <Directory> (wobei ich mir beim letzten nicht sicher bin) regeln kann, dass nur bestimmten php Seiten (z.B. "intern/*.php") der Zugriff gewährt wird und für den Rest ein Deny from all gilt.

          Noch besser - aber nicht zwingend notwendig - wäre es, wenn für alle Zugriffe außerhalb der "intern/*.php" - Dateien das von dir beschriebene Popup-Fenster auftaucht und ein .htpasswd - Passwort abfrägt, damit ich als Admin mir auch so die Dateien anschauen könnte. Muss aber wie gesagt nicht sein.

          Kennst du dich da aus?

          Vielen Dank und liebe Grüße,

          Wolfgang

          1. Hi,

            Ich hab auf verschiedenen Seite gelesen, dass man das über Direktiven, wie <Files>, <FilesMatch> oder <Directory> (wobei ich mir beim letzten nicht sicher bin) regeln kann, dass nur bestimmten php Seiten (z.B. "intern/*.php") der Zugriff gewährt wird und für den Rest ein Deny from all gilt.

            Mit DENY/ALLOW regelst du Zugriffe über HTTP.
            Dein PHP-Script greift aber über das Dateisystem auf Dateien zu.
            Also besteht da überhaupt kein Zusammenhang.

            Wenn dein PHP-Script für den Nutzer aufrufbar ist, und diesem die Dateiinhalte durchreicht = dann brauchst du einfach nur den Zugriff auf diese Dateien über HTTP unterbinden.

            Noch besser - aber nicht zwingend notwendig - wäre es, wenn für alle Zugriffe außerhalb der "intern/*.php" - Dateien das von dir beschriebene Popup-Fenster auftaucht und ein .htpasswd - Passwort abfrägt, damit ich als Admin mir auch so die Dateien anschauen könnte. Muss aber wie gesagt nicht sein.

            Dann tausche einfach das komplette Untersagen des Zugriffs über HTTP durch eine "Passwortabfrage" (HTTP Authentication nennt sich das korrekt) aus.

            MfG ChrisB

            --
            Light travels faster than sound - that's why most people appear bright until you hear them speak.
            1. Hey Chris,

              Dann tausche einfach das komplette Untersagen des Zugriffs über HTTP durch eine "Passwortabfrage" (HTTP Authentication nennt sich das korrekt) aus.

              Ich habe jetzt folgende .htaccess dem Unterordner hinzugefügt:

              authUserFile (Pfad zur public_html)/intern/dataintern/.htpasswd
              AuthName "Bitte Zugangsdaten eingeben!"
              AuthType Basic
              require valid-user

              Wenn ich jetzt als Admin auf den Ordner zugreifen will, muss ich wie erwartet ein Passwort eingeben...
              Das Problem ist, dass User, die auf einer Seite wie z.B. "intern/index.php" den Link zu einer Datei in "dataintern" anklicken ebenfalls das Passwort eingeben müssen...

              Hier hätte ich eben gerne eine Ausnahme erstellt, dass alle php Seiten aus dem Ordner "intern" kein Passwort eingeben müssen.

              Hoffe so langsam sickert durch, wie ich das gemeint habe...

              Vielen Dank für die Bemühungen und noch einen schönen Abend,

              Wolfgang

              1. Hallo,

                Wenn ich jetzt als Admin auf den Ordner zugreifen will, muss ich wie erwartet ein Passwort eingeben...

                und kommst dann auch an die Daten, die du haben willst? - Gut.

                Das Problem ist, dass User, die auf einer Seite wie z.B. "intern/index.php" den Link zu einer Datei in "dataintern" anklicken ebenfalls das Passwort eingeben müssen...

                Natürlich. Deswegen hatte ich ja schon gesagt, dass du ein Download-Script schreiben musst, das die Dateien aus diesem Verzeichnis rausklaubt, denn mit HTTP ist der Zugriff ja nun eingeschränkt.

                Hier hätte ich eben gerne eine Ausnahme erstellt, dass alle php Seiten aus dem Ordner "intern" kein Passwort eingeben müssen.

                Das ist unmöglich! Es ist ja *eben nicht* das PHP-Script, das diese Datei anfordert, sondern der Browser des Besuchers, nachdem eben dieser Besucher einen Link angeklickt hat.
                Dein Server sieht einen HTTP-Request, der eine Ressource aus dem Verzeichnis anfordert, und reagiert korrekt, wenn er eine Authentifizierung verlangt, weil er die Kausalität dieses Requests nicht kennt, nicht kennen *kann*.

                Hoffe so langsam sickert durch, wie ich das gemeint habe...

                Schon lange. Aber mir scheint, du hast noch ein Verständnisproblem, was die serverinternen bzw. durch den Client veranlassten Vergänge betrifft: Es gibt in HTTP kein "von" [Cheatah]. Jeder Request darf und muss für sich allein betrachtet werden und hat keinen Zusammenhang zu Vorgängen, die sich vorher ereignet haben.

                Und wenn dir jetzt jemand empfehlen will, den Referer auszuwerten: Das ist auch keine zuverlässige Maßnahme. Das mag unter Laborbedingungen gehen, wo du die Eigenheiten und Einstellungen aller Clients unter Kontrolle hast. "Draußen im bösen Internet" ist das zum Scheitern verurteilt, weil der Referer ebenso leicht zu fälschen ist (manchmal nicht einmal bewusst) wie der User Agent oder die Absenderadresse einer e-Mail.

                So long,
                 Martin

                --
                Wissen erwirbt man, indem man immer das Kleingedruckte sorgfältig liest.
                Erfahrung bekommt man, indem man das nicht tut.
                1. Da bin ich mal wieder...

                  Ich glaube, ich habs mittlerweile kapiert, warum das erstmal nicht gehen kann.

                  Deswegen hatte ich ja schon gesagt, dass du ein Download-Script schreiben musst, das die Dateien aus diesem Verzeichnis rausklaubt,...

                  Wie würde das dann ungefähr aussehen, bzw. wie spreche ich die Dateien an, anstatt sie über <a href="dataintern/test.zip">Test</a> zu verlinken? Und ist diese Methode dann nicht auch von anderen möglich?

                  Hoffe, ich nerv nicht allzu sehr...

                  Noch einen guten Morgen,

                  Wolfgang

                  1. Moin,

                    Ich glaube, ich habs mittlerweile kapiert, warum das erstmal nicht gehen kann.

                    gut, so langsam fügt sich also das Puzzle zusammen.

                    »» Deswegen hatte ich ja schon gesagt, dass du ein Download-Script schreiben musst, das die Dateien aus diesem Verzeichnis rausklaubt,...
                    Wie würde das dann ungefähr aussehen, bzw. wie spreche ich die Dateien an, anstatt sie über <a href="dataintern/test.zip">Test</a> zu verlinken?

                    Du erstellst ein weiteres PHP-Script mit dem gleichen Login-Mechanismus wie die bisherigen. Als "Nutzcode" enthält es nur wenige Zeilen:

                    $allowed = array("test.zip", "data.zip", "readme.txt");   // erlaubte Dateien für Download  
                      
                    if (isset($_GET['file']))                                 // wurde ein file-Parameter übergeben?  
                     { if (in_array($_GET['file'], $allowed))                 // ist die Datei zum Download freigegeben?  
                        { header("Content-Type: application/octet-stream");   // geeigneten HTTP-Header setzen  
                          readfile("dataintern/" . $_GET['file']);            // Datei durchreichen  
                        }  
                       else                                                   // ungültiger Parameter  
                        { echo "Fehler: Zugriff auf angegebene Datei nicht erlaubt.\n";  
                        }  
                    else                                                      // gar kein file-Parameter  
                     { echo "Fehler: Kein Dateiname angegeben.\n";  
                     }
                    

                    Und statt mit http://example.org/dataintern/test.zip referenzierst du die Datei nun mit http://example.org/download.php?file=test.zip, sonst ändert sich "von außen" nichts.
                    Wichtig ist hier vor allem zu verhindern, dass jemand durch "geschickte" Pfadangaben im file-Parameter Daten ausspionieren kann, die er nicht haben darf. Das verhindere ich, indem ich den Parameter gegen ein Array von erlaubten Werten prüfe und alle anderen Versuche als ungültig abweise.

                    Wichtig ist außerdem, dass das Script im Erfolgsfall vor diesen paar Zeilen keine Ausgabe an den Browser senden darf, weil sonst die header()-Anweisung ihre Aufgabe nicht mehr erledigen kann. Aber das ist ja bekannt ... ;-)

                    Und ist diese Methode dann nicht auch von anderen möglich?

                    Wie meinst du das?

                    So long,
                     Martin

                    --
                    Küssen ist die schönste Methode, eine Frau zum Schweigen zu bringen.
                    1. Hey Leute,

                      entschuldigt, dass es dieses Mal so lange gedauert hat mit antworten.

                      »» Und ist diese Methode dann nicht auch von anderen möglich?
                      Wie meinst du das?

                      Damit war gemeint, wie ich verhinden kann, dass man z.B. durch Einfügen der Zeilen:

                          { header("Content-Type: application/octet-stream");   // geeigneten HTTP-Header setzen  
                            readfile("www.exaple.org/dataintern/" . $_GET['file']);   
                      

                      von einer fremden Seite den Zugriff sperren kann.

                      Ich habe das Ganze jetzt wie folgt umgesetzt: (Falls mal jemand ein ähnliches Problem hat)

                        
                      <?php  
                      session_start();  
                      if($_SESSION["loggedin"] == TRUE) {                                                    // wird von meiner auth.php gesetzt  
                        
                      	$allowedDir  = array("dir1","dir2","dir3");		                    // erlaubte Verzeichnisse für den Download	  
                      	  
                      	// erlaubte Dateien für den Download  
                      	$allowedFile = array("file1.pdf","file2.zip","file3.jpg");  
                      	  
                      	if ( isset($_GET['dir']) && isset($_GET['file']) ) {                           // Wurden die Parameter übergeben?  
                      	   $dir = $_GET['dir'];  
                      	   $file = $_GET['file'];  
                      	  
                      	   if ( in_array($dir, $allowedDir) && in_array($file, $allowedFile) ) {       // Sind die Parameter erlaubt?  
                      	      header("Content-Type: application/force-download");                      // geeigneten HTTP-Header setzen  
                      	      header("Content-disposition: attachment; filename=\"".$file."\"");  
                      	      readfile($dir."/".$file);   	             			// Datei durchreichen  
                      	   } else {                                                                    // ungültiger Parameter  
                                   echo "Fehler: Zugriff auf angegebene Datei nicht erlaubt.\n";  
                                 }  
                              } else {                                                                       // gar kein file-Parameter  
                                echo "Fehler: Kein Dateiname angegeben.\n";  
                              }  
                      } else {  
                        echo "Sie sind nicht eingeloggt.\n"  
                      }  
                      ?> 
                      

                      Zusätlich befindet sich noch in jedem Verzeichnis dir1, dir2 und dir3 eine .htaccess Datei, die ein Admin-Kennwort abfragt, damit ich auf die Dateien zugreifen kann (Man kann theoretisch auch im übergeordneten Verzeichnis eine .htaccess anlegen).

                      Falls es da noch Verbesserungsvorschläge gibt, so bin ich für alles offen... :)

                      Ansonsten bedanke ich mich für die intensive Hilfe und wünsche noch eine gute Nacht,

                      Wolfgang

                      1. Hallo,

                        »» »» Und ist diese Methode dann nicht auch von anderen möglich?
                        »» Wie meinst du das?
                        Damit war gemeint, wie ich verhinden kann, dass man z.B. durch Einfügen der Zeilen:

                        { header("Content-Type: application/octet-stream");   // geeigneten HTTP-Header setzen

                        readfile("www.exaple.org/dataintern/" . $_GET['file']);

                        
                        > von einer fremden Seite den Zugriff sperren kann.  
                          
                        wieso von einer \*fremden\* Seite? Wir waren uns doch schon einig, dass der HTTP-Zugriff auf das Verzeichnis /dataintern komplett unterbunden wird.  
                          
                        
                        > Ich habe das Ganze jetzt wie folgt umgesetzt: (Falls mal jemand ein ähnliches Problem hat)  
                        >   
                        > ~~~php
                          
                        
                        > <?php  
                        > session_start();  
                        > if($_SESSION["loggedin"] == TRUE) {                                                    // wird von meiner auth.php gesetzt  
                        >   
                        > 	$allowedDir  = array("dir1","dir2","dir3");		                    // erlaubte Verzeichnisse für den Download	  
                        > 	  
                        > 	// erlaubte Dateien für den Download  
                        > 	$allowedFile = array("file1.pdf","file2.zip","file3.jpg");  
                        > 	  
                        > 	if ( isset($_GET['dir']) && isset($_GET['file']) ) {                           // Wurden die Parameter übergeben?  
                        > 	   $dir = $_GET['dir'];  
                        > 	   $file = $_GET['file'];  
                        > 	  
                        > 	   if ( in_array($dir, $allowedDir) && in_array($file, $allowedFile) ) {       // Sind die Parameter erlaubt?  
                        > 	      header("Content-Type: application/force-download");                      // geeigneten HTTP-Header setzen  
                        > 	      header("Content-disposition: attachment; filename=\"".$file."\"");  
                        > 	      readfile($dir."/".$file);   	             			// Datei durchreichen  
                        > 	   } else {                                                                    // ungültiger Parameter  
                        >              echo "Fehler: Zugriff auf angegebene Datei nicht erlaubt.\n";  
                        >            }  
                        >         } else {                                                                       // gar kein file-Parameter  
                        >           echo "Fehler: Kein Dateiname angegeben.\n";  
                        >         }  
                        > } else {  
                        >   echo "Sie sind nicht eingeloggt.\n"  
                        > }  
                        > ?> 
                        
                        

                        Okay. Eins nach dem anderen.

                        if($_SESSION["loggedin"] == TRUE)

                        Der explizite Vergleich eines boolschen Wertes mit der Konstanten true ist sinnfrei - ein einfaches 'if ($_SESSION["loggedin"])' erfüllt den gleichen Zweck und ist besser lesbar.

                        $allowedDir  = array("dir1","dir2","dir3")
                        $allowedFile = array("file1.pdf","file2.zip","file3.jpg");

                        Ah, du willst verschiedene Verzeichnisse bedienen. Kann man so machen; ich würde aber die Kombinationen aus Verzeichnis und Dateinamen als Einheit sehen und nicht getrennt abfragen. Bedenke, dass mit deinem Ansatz *jede* Kombination aus Verzeichnis und Dateiname akzeptiert wird - in deinem Beispiel neun verschiedene Kombinationen, die bestimmt nicht alle existieren.

                        $dir = $_GET['dir'];
                           $file = $_GET['file'];

                        Wozu das Umkopieren?

                        header("Content-Type: application/force-download");
                              header("Content-disposition: attachment; filename="".$file.""");

                        Das sieht man ab und zu - aber im Gegensatz zu application/force-download, was frei erfunden ist, ist application/octet-stream standardisiert. Die nachfolgende Headerzeile Content-Disposition wäre nur nötig, wenn du einen Content-Type angibst, bei dem ein Browser auf die Idee kommen könnte, ihn selbst darzustellen.

                        Gute Nacht ebenfalls,
                         Martin

                        --
                        Success should be measured not so much by the position that one has reached in life,
                        but by the obstacles one has overcome while trying to succeed.