Nginx: Verstecke Dateien und Ordner nicht ausliefern

Eine eigentlich typische Anforderung: Versteckte Dateien und Ordner (ihnen wird unter Linux ein Punkt (".") vorangestellt) sollen nicht über den Webserver abrufbar sein. Beim Apache ist diese Anforderung standardmäßig für Dateien, die mit ".ht" beginnen, umgesetzt - bei Nginx hingegen überhaupt nicht.

Meine erste Konfiguration, dies zu lösen, sah wie folgt aus:

1
2
3
  location ~ ^(.*)\/\.(.*)$ {
    return 404;
  }

Hier wird geprüft, ob, im Dateinamen ein Slash gefolgt von einem Punkt enthalten ist. Oder anders ausgedrückt: Ob irgendwo eine Datei/ein Ordner im Dateinamen steckt, der mit einem Punkt beginnt. Spannenderweise hat diese Variante einen Harken: Sie funktioniert nicht für Dateien, die sich in einem versteckten Ordner befinden. Warum das so ist, weiß ich persönlich nicht, denn wenn man den gleichen regulären Ausdruck in einem Rewrite verwendet, funktioniert es, wie gewünscht:

1
2
3
4
5
  rewrite ^(.*)\/\.(.*)$ @404 break;

  location = @404 {
    return 404;
  }

Falls also jemand eine Idee hat, warum "location" anders reagiert als "rewrite": Immer her damit! Ich bin auf eure Erklärungen gespannt. 🙂
Versteckte Grüße, Kenny

10 Kommentare » Schreibe einen Kommentar

    • Ach "greedy" war dieses Wort, was ich vergaß. Egal, ich denke, die kurze Schreibweise ist aber im Falle von nginx dennoch weniger fehleranfällig, wie dein Test ja unschwer bewies.

      Kein Dogma, aber: nginx' Schlankheit sollte sich auch in den Konfigurationsdateien widerspiegeln. Monströse Regexp können sicher auch zur "negativen Leistung" beitragen, den Regexp ist immer teuer. (Und als Entwickler bin ich sowieso notorisch [schreib]faul … ;o)

      Zur 403/404-Angelegenheit: das ist wohl eher eine Glaubensfrage, da gibt es keine absolute Wahrheit.

  1. Theoretisch sind in beiden Fällen die Regeln sehr weitreichend und ungenau und überlässt dem Regexp-Parser die Wahl, wie er damit umgehen soll. Ich kenne das Problem in der Programmierung allzu oft.

    Eine Regel wie oben /^(.*)/.(.*)$/ kann für einen Pfad theoretisch sehr vielseitig ausgelegt werden, was an dem "Allesfresser" (.*) liegt.

    Pfade wie /path/to-a/.hidden/file könnten nun wie folgt durchgeparst werden:

    ^{/path/to-a/.hidden/file}{}$ - sprich: der erste Matcher könnte einfach den ganzen Pfad für sich beanspruchen, da /(.*)/ keine Ausnahmen enthält.

    Hierbei geht man davon aus, das reguläre Ausdrücke immer von vorn nach hinten durchgeparst werden, was sie mE auch in fast allen Implementationen auch erst einmal tun; jedenfalls habe ich mit solchen Formulierungen oft unerwünschte Ergebnisse.

    Besser ist es, statt /(.*)/ einen Ausdruck zu verwenden, der definitiv nicht alles frisst, sondern etwas ausschließt, was nicht mehr enthalten sein darf, weil ein nachfolgender Ausdruck sich dieser Bedingung annehmen soll.

    In deinem Fall wäre die Bedingung //./ (Slash mit folgendem Punk) ja die Schlüsselbedingung, aber der ein Pfad nicht mehr ausgeliefert werden darf.

    Für einzelne Zeichen ist das Schreiben solcher Bedingungen einfach, und wird in selbst geschriebenen HTML-Tag-Parsern ja oft und gern angewandt (sollte es zumindest).

    So lauten ja die Regeln meist irgendwie so: /]+)>/ - was übersetzt ja nur soviel heisst wie: Bedingung 1 = öffnende Spitzklammer; Bedingung 2 = alle Zeichen, die keine ("^" = NOT) schließende Spitzklammer sind, mindestens jedoch 1 Zeichen muss es sein; Bedingung 3: schließende Spitzklammer.

    Jedoch muss man sich ja nicht auf nur ein einzelnes Zeichen beschränken, reguläre Ausdrücke sind ja ein mächtiges Werkzeug.

    Eine Regel, wie die von dir gewünschte könnte demnach lauten: /[^/][^.](.*)/ (ungetestet!)

    Ich habe hier bewusst auf die Begrenzer verzichtet, da es die Regel damit vereinfachen sollte. Wir überprüfen ja letztlich nur, ob im Pfad die Zeichenkette "/." vorkommt oder nicht (mit der Zusatzbedingung, dass danach noch Zeichen folgen können).

    Damit sollten eigentlich sämtliche von *nixoiden Systemen versteckten Dateien/Ordner gefiltert werden.

    Achja, die obige Regel für nginx geschrieben lautet wohl dann nur noch: /. (ein Slash muss wohl nicht "eskapiert" werden, sondern nur der Punkt), einen Allesfresser bedarf es überhaupt nicht, da location immer eine Art regulärer Ausdruck ist, sonst müsste man ja jeden Pfad, den es geben könnte, extra maskieren.

    Wenn das also funktioniert, was die vielen Google-Ergebnisse mir belegen, dann ist hier weniger also mehr.

    Ich verwende z. B. von einem Standard-Template diese Regel hier:

    1
    2
    3
    4
    ## Disable viewing .htaccess & .htpassword
            location ~ /.ht {
                    deny  all;
            }

    Das "deny all" finde ich in diesem Kontext auch sauberer, denn wir wollen ja nicht den Anschein erwecken, es gäbe die Datei nicht, wenn sie doch existierte, sondern es wird einfach der richtige Fehler "403 Forbidden" geworfen, immer! Sogar viel besser, wie ich finde. Und das maskieren des Punktes wurde hier ebenfalls nicht vorgenommen, und es funktioniert dennoch.

    Wieso gibt es eigentlich keinen pre- und code-Filter für die Kommentarfunktion? Wäre in einem Code-lastigen Blog ja von Vorteil, oder? ;o)

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.