Zugriffsbeschränkungen

Wer Plugins oder Themes selber schreibt wird sich früher oder später auch Gedanken über die Dateizugriffe machen müssen. Nicht jeder soll von Außen Zugriff auf unsere Dateien haben, wir möchten doch ganz gerne steuern wer welche Datei aufrufen darf.

Meistens ist ein Aufruf a lá http://www.example.com/wp-content/plugins/a-plugin/conection_data.php eher harmlos, da versucht wird das Script auszuführen anstatt es anzuzeigen. Nicht zuletzt bleibt aber jedoch die Möglichkeit via include(_once) und require(_once) das Script auch von entfernten Servern aus einzubinden und auszuführen.
Wie dem auch sei, ein mulmiges Gefühl bleibt immer zurück wenn jeder auf alle möglichen Dateien einfach per Browser oder PHP-Funktion zugreifen kann. Daher ist es bereits jetzt Best Practice seine Scripte und Dateien mit einer Kontrolle vor solch ungewollten Zugriffe zu schützen.

In vielen Plugins sieht man solchen und vergleichbaren Code

<?php 
  if ( ! defined( 'My_PLUGIN_VAR' ) ) 
    die( 'No direct script access allowed' );
?>

In der Plugin-Datei wird eine Konstante definiert (MY_PLUGIN_VAR) und in allen anderen Dateien wird abgefragt ob diese Konstante definiert ist. Ist sie es nicht, wurde womöglich direkt auf diese Datei zugegriffen was unerwünscht ist. Es gibt hier verschiedene Techniken diese Kontrolle durchzuführen.
Und die Plugin-Datei selber will ja auch noch davor geschützt sein direkt aufgerufen zu werden:

<?php 
  if ( ! function_exists( 'add_action' ) ) 
    die( 'No direct script access allowed' );
?>

Falls die Funktion add_action nicht definiert wurde, was darauf schließen lässt das WordPress nicht gestartet wurde, kann man von einem direkten Zugriff auf die Plugin-Datei ausgehen. Auch hier gibt es verschiedene Wege um zu prüfen ob WordPress bereits gestartet wurde oder nicht.

Das ist alles schön und gut, aber auch aufwendig und fehleranfällig. Stellen wir uns mal ein Plugin mit mehreren Unterverzeichnissen und in jedem Unterverzeichnis wiederum mehreren Dateien vor. Arbeitet man alleine an dem Plugin, ist es schon schwer den Überblick zu behalten ob man wirklich jede Datei geschützt hat. Bei einem Projekt an denen mehrere Personen mitarbeiten ist die Gefahr um so größer das einer der Mitarbeiter vergisst einen entsprechenden Schutz einzubauen. Und so wie Murphy es will, ist es ausgerechnet die Datei, mit den größten Schwachstellen die niemand kontrolliert hat.
Hinzu kommt das man mit diesen Techniken unter Umständen nicht jede Datei schützen kann. In JavaScript-Dateien wird es schon schwer mit diesen Techniken einen unerwünschten Zugriff zu verhindern. Spätestens bei CSS-Dateien müsste man gänzlich aufgeben.

Besser wäre es wenn man zentral regeln könnte auf welche Dateien zugegriffen werden darf und welche nicht. Der Apache Webserver bietet uns solch eine Möglichkeit in Form von .htaccess. Dazu benötigen wir im Grunde genommen nur zwei Regeln. Eine die die erlaubten Zugriffe beinhaltet und eine die die unerlaubten Zugriffe beinhaltet.

<FilesMatch "\.(php|xml)$">
    Order allow,deny
    Deny from all
</FilesMatch>

<FilesMatch "(\.txt|\.htm?l|index\.php)$">
  Order allow,deny
  Allow from all
</FilesMatch>

Im ersten Block verbieten wir sämtliche Zugriffe auf alle PHP- und XML-Dateien. Die Liste kann man noch um weitere Dateitypen erweitern, je nach Bedarf. Da wir aber nicht alle Zugriffe grundsätzlich sperren wollen, heisst ja readme.txt und nicht readme_not.txt, erlauben wir im zweiten Block den Zugriff auf bestimmte Dateitypen (.txt und .htm[l]), sowie auf die index.php. Auch hier kann man je nach Bedarf bestimmte Dateitypen und Dateien hinzufügen bzw. entfernen.
Das Beste an der Sache ist aber, es wirkt sich auch auf alle Unterverzeichnisse aus, wir müssen uns also keine Gedanken darüber machen ob nicht zufällig die eine Datei, die die Schwachstelle im System darstellt, nicht geschützt ist.

Wo viel Licht ist, ist natürlich auch viel Schatten. Denn auf anderen Webservern als den Apache (z.B. lighttpd, IIS, usw.) funktioniert das natürlich nicht. Entweder man legt für jeden Webserver dann entsprechende Konfigurations-Dateien an, sofern möglich, prüft auf die Server-Software und verweigert den Dienst oder nutzt diese Technik nur zusätzlich um auf Nummer sicher zu gehen.

Keine Trackbacks

2 Kommentare

  1. Jannik

    Hallo,

    bei dem RegEx für den zweiten Block ist ein Teil falsch: “\.html?” anstatt “\.htm?l”.
    Tendenziell würde ich empfehlen, erst einmal *alles* zu sperren und dann nur begrenzt Dinge zuzulassen (also eine Whitelist anstatt einer Blacklist).

    Veröffentlicht 25. Juli 2011 am 21:05 | Permalink
  2. Kaiser

    Servus Ralf,

    nicht zu vergessen dieses Snippet, weil nach Klassen zu suchen schneller ist, als Funktionsexistenz abzuprüfen:

    if ( !class_exists(‘WP’) )
    {
    header( ‘Status: 403 Forbidden’ );
    header( ‘HTTP/1.1 403 Forbidden’ );
    exit;
    }

    Auch interessant ist dieses Snippet, dass ich im “Carrington Core” gefunden habe:
    if (__FILE__ == $_SERVER['SCRIPT_FILENAME']) { die(); }

    Veröffentlicht 31. Juli 2011 am 18:10 | Permalink