cvs: phpdoc-de /security index.xml

From: Date: Tue, 24 Jun 2003 22:33:29 +0000
Subject: cvs: phpdoc-de /security index.xml
Groups: php.doc.de 
Request: Send a blank email to doc-de+get-762@lists.php.net to get a copy of this message
betz Tue Jun 24 18:33:29 2003 EDT Added files: /phpdoc-de/security index.xml Log: Moved from chpaters/security.xml

Index: phpdoc-de/security/index.xml +++ phpdoc-de/security/index.xml <?xml version="1.0" encoding="iso-8859-1"?> <!-- $Revision: 1.1 $ --> <!-- EN-Revision: 1.51 Maintainer: tom Status: ready --> <chapter id="security.index"> <title>Sicherheit</title> <simpara> PHP ist eine mächtige Sprache, und der Interpreter, der in einen Webserver als Modul oder als separate <acronym>CGI</acronym>-Version eingebunden werden kann, kann auf Dateien zugreifen, Befehle ausführen und Netzwerkverbindungen zu einem Server herstellen. Diese Eigenschaften können einen Webserver unsicher machen, wenn man es bei den Voreinstellungen belässt. PHP wurde besonders dafür entwickelt, um eine sicherere Sprache als Perl oder C für die Erstellung von CGI-Programmen bereitzustellen. Mit der richtigen Wahl der Einstellungen beim Kompilieren und zur Laufzeit bietet PHP genau die Kombination aus Freiheit und Sicherheit, die gerade benötigt wird. </simpara> <simpara> Da es sehr viele verschiedene Möglichkeiten gibt, PHP zu nutzen, gibt es viele Konfigurationseinstellungen, die das Verhalten von PHP beeinflussen. Eine große Auswahl an Einstellungen garantiert, dass man PHP für viele Zwecke einsetzen kann. Allerdings bedeutet das auch, dass es Kombinationen gibt, die eine Installation mit nur ungenügender Sicherheit zur Folge haben. </simpara> <simpara> Die Flexibilität der Konfiguration konkurriert mit der Flexibilität in der Programmierung. Mit PHP können komplette Server Applikationen mit allen Möglichkeiten eines Shell Benutzers erstellt werden, oder auch nur einfache Server Side Includes mit einem minimalen Risiko in einer streng kontrollierten Umgebung. Wie die Umgebung erstellt wird, und wie sicher diese ist, ist zu einem großen Teil die Sache des PHP Entwicklers. </simpara> <simpara> Dieses Kapitel beginnt mit einigen generellen Ratschlägen zur Sicherheit, erklärt die verschiedenen Kombinationen der Konfigurationseinstellungen und unter welchen Gegebenheiten sie sicher genutzt werden können, und beschreibt verschiedene Überlegungen zur Programmierung für verschiedene Sicherheitsstufen. </simpara> <sect1 id="security.general"> <title>Allgemeine Überlegungen</title> <simpara> Ein komplett sicheres System ist praktisch ein Ding der Unmöglichkeit, weshalb ein unter Sicherheitsprofis oft genutzter Ansatz ist, einen Mittelweg zwischen Risiko und Verwendbarkeit zu finden. Wenn jede von einem Benutzer übermittelte Variable zwei Formen von biometrischer Prüfung (wie z.B. ein Scan der Netzhaut und ein Fingerabdruck) verlangen würde, wäre eine extrem hohe Ebene der Verantwortlichkeit erreicht. Ein sehr komplexes Formular auszufüllen würde auch eine halbe Stunde in Anspruch nehmen, die Benutzer dazu ermuntern könnte, Wege zur Umgehung der Sicherheitsmaßnahmen zu suchen. </simpara> <simpara> Die beste Sicherheit ist oft unaufdringlich genug um den Anforderungen zu entsprechen, ohne den Benutzer an seiner Arbeit zu hindern oder den Code-Autor mit übertriebener Komplexität zu überlasten. Tatsächlich sind einige Sicherheitsangriffe nur die Folge von allzu strengen Sicherheitsmaßnahmen, was mit der Zeit nur zu deren Unterminierung führt. </simpara> <simpara> Eine Phrase die es wert ist, sich an sie zu erinnern: Ein System ist nur so gut wie das schwächste Glied in der Kette. Wenn alle Transaktionen mittels Zeit, Ort, Transaktionstyp, etc. streng mitprotokolliert werden, der Benutzer aber nur mittels einem einzigen Cookie verifiziert wird, lässt die Zuverlässigkeit für die Bindung des Benutzers an das Transaktions-Log bedrohlich nach. </simpara> <simpara> Denken Sie während der Tests daran, dass Sie selbst für die einfachsten Seiten nicht alle Möglichkeiten testen können. Der von Ihnen vielleicht erwartete Input wird zu dem eines verstimmten Mitarbeiters oder eines Crackers der Monate Zeit hat, oder einer Katze, die über die Tastatur läuft in keinerlei Zusammenhang stehen. Deshalb betrachten Sie Ihren Code am Besten aus der logischen Perspektive um zu erkennen, wo unerwartete Daten eingebracht werden können und fragen sich dann, wie diese modifiziert, reduziert, oder weiter ausgeführt werden. </simpara> <simpara> Das Internet ist voll von Leuten die versuchen, sich durch Entschlüsseln/zerstören Ihres Codes, den Zusammenbruch Ihres Systems, Einsetzen von unangebrachten Inhalten, und anderen, Ihren Tag interessant gestaltenden Maßnahmen, einen Namen zu machen. Es ist egal, ob Sie eine kleine oder große Site haben, Sie sind einfach ein Ziel wenn Sie online sind oder wenn Sie einen Server haben, zu dem man eine Verbindung aufbauen kann. Viele Cracker-Programme erkennen nicht die Größe, sondern durchsieben einfach gewaltige IP Blöcke im Netz, um Opfer zu finden. Versuchen Sie, keines zu werden. </simpara> </sect1> <sect1 id="security.cgi-bin"> <title>CGI-Version</title> <sect2 id="security.cgi-bin.attacks"> <title>Mögliche Angriffe</title> <simpara> PHP als <acronym>CGI</acronym> zu nutzen, ist eine Möglichkeit für Installationen, bei denen aus irgendwelchen Gründen kein Modul in die Serversoftware eingebunden werden soll (wie beim Apache) oder für Systeme, bei denen verschiedene CGI-Wrapper genutzt werden sollen, um sichere chroot- und setuid-Umgebungen für Skripte zu schaffen. Bei dieser Konfiguration wird das ausführbare PHP-Binary üblicherweise im cgi-bin Verzeichnis des Webservers installiert. CERT advisory <ulink url="&url.cert;">CA-96.11</ulink> spricht sich gegen die Platzierung von Interpretern im cgi-bin Verzeichnis aus. Obwohl das PHP-Binary als eigenständiger Interpreter verwendet werden kann, wurde PHP so entwickelt, um den durch diese Konfiguration möglich werdenden Angriffe vorzubeugen: </simpara> <itemizedlist> <listitem> <simpara>Zugriff auf Systemdateien: <filename role="url">http://my.host/cgi-bin/php?/etc/passwd</filename> </simpara> <simpara> Die auf ein Fragezeichen (?) folgende Abfrageinformation in einer URL wird durch das CGI-Interface als Kommandozeilenargument an den Interpreter weitergereicht. In der Kommandozeile wird üblicherweise die im ersten Argument angegebene Datei von Interpretern geöffnet und ausgeführt. </simpara> <simpara> Beim Aufruf als CGI-Binary verweigert PHP die Interpretation der Kommandozeilenargumente. </simpara> </listitem> <listitem> <simpara> Zugriff auf beliebige Web-Dokumente auf dem Server: <filename role="url">http://my.host/cgi-bin/php/secret/doc.html</filename> </simpara> <simpara> Der Teil der URL-Pfadinformation nach dem Namen der PHP Binärdatei, <filename role="uri">/secret/doc.html</filename> wird im allgemeinen benutzt, um den Namen der Datei zu übergeben, die durch das <acronym>CGI</acronym>-Programm geöffnet und interpretiert werden soll. Normalerweise werden einige Einträge in der Konfigurationsdatei des Webservers benutzt (Apache: Action), um Aufrufe von Dokumenten wie <filename role="url">http://my.host/secret/script.php3</filename> an den PHP-Interpreter umzuleiten. Bei dieser Konfiguration überprüft der Webserver zuerst die Zugriffsrechte im Verzeichnis <filename class="directory" role="uri">/secret</filename> und erstellt anschließend den umgeleiteten Aufruf <filename role="url">http://my.host/cgi-bin/php/secret/script.php</filename>. Unglücklicherweise wird, wenn der Aufruf bereits in dieser Form geschieht, vom Webserver keine Zugriffsüberprüfung der Datei <filename role="uri">/secret/script.php</filename>, sondern lediglich der Datei <filename role="uri">/cgi-bin/php</filename> vorgenommen. So ist jeder Benutzer, der auf <filename role="uri">/cgi-bin/php</filename> zugreifen darf in der Lage, sich zu jedem geschützten Dokument auf dem Webserver Zugriff zu verschaffen. </simpara> <simpara> Bei PHP können beim Kompilieren die Konfigurationsoption <link linkend="install.configure.enable-force-cgi-redirect">--enable-force-cgi-redirect</link> und zur Laufzeit die Konfigurationsdirektiven <link linkend="ini.doc-root">doc_root</link> und <link linkend="ini.user-dir">user_dir</link> benutzt werden, um diesen Angriff zu verhindern, falls der Verzeichnisbaum des Servers Verzeichnisse mit Zugriffsbeschränkungen beinhaltet. Ausführliche Informationen über die verschiedenen Kombinationen siehe weiter unten. </simpara> </listitem> </itemizedlist> </sect2> <sect2 id="security.cgi-bin.default"> <title>Fall 1: Nur öffentliche Dateien vorhanden</title> <simpara> Wenn der Server keine Inhalte hat, die durch Passwort oder IP-basierte Zugriffskontrolle geschützt sind, werden diese Konfigurationsoptionen nicht benötigt. Wenn der Webserver keine Redirects erlaubt oder keine Möglichkeit hat, dem PHP-Binary mitzuteilen dass es sich um eine sicher umgeleitete Anfrage handelt, kann die Option <link linkend="install.configure.enable-force-cgi-redirect">--enable-force-cgi-redirect</link> im configure-Script angegeben werden. Nichtsdestotrotz müssen Sie sicherstellen, dass Ihre PHP-Skripte nicht auf die eine oder andere Art des Aufrufs angewiesen sind, weder direkt durch <filename role="php">http://my.host/cgi-bin/php/dir/script.php</filename> noch durch einen Redirect <filename role="php">http://my.host/dir/script.php</filename>. </simpara> <simpara> Beim Apache kann der Redirect durch den Gebrauch von AddHandler und Action konfiguriert werden (siehe unten). </simpara> </sect2> <sect2 id="security.cgi-bin.force-redirect"> <title>Fall 2: --enable-force-cgi-redirect benutzen</title> <simpara> Diese beim Kompilieren verwendete Option verhindert grundsätzlich den Aufruf von PHP mit einer URL wie <filename role="php">http://my.host/cgi-bin/php/secretdir/script.php</filename>. Stattdessen parst PHP in diesem Modus nur dann, wenn der Aufruf durch einen korrekten Redirect des Webservers erfolgte. </simpara> <simpara> Normalerweise wird der Redirect in der Apache-Konfiguration mit den folgenden Einträgen festgelegt:</simpara> <programlisting role="apache-conf"> <![CDATA[ Action php-script /cgi-bin/php AddHandler php-script .php ]]> </programlisting> <simpara> Diese Option wurde nur mit dem Apache Webserver getestet und ist abhängig davon, wie Apache die nicht standardmäßige CGI-Umgebungsvariable <envar>REDIRECT_STATUS</envar> bei Redirect-Anfragen setzt. Sollte Ihr Webserver keine Möglichkeit unterstützen, zu übermitteln, ob es sich um einen direkte Aufruf oder einen Redirect handelt, können Sie diese Option nicht verwenden und müssen einen der anderen hier beschriebenen Wege gehen, die CGI-Version zu nutzen. </simpara> </sect2> <sect2 id="security.cgi-bin.doc-root"> <title>Fall 3: doc_root oder user_dir festlegen</title> <simpara> Aktiven Inhalt, wie beispielsweise Skripts und ausführbare Dateien, in den Dokumentverzeichnissen des Webservers abzulegen, wird manchmal als unsichere Methode angesehen. Wenn, beispielsweise aufgrund von Konfigurationsfehlern, die Skripte nicht ausgeführt, sondern als reguläre HTML-Dokumente angezeigt werden kann dies ein Durchsickern von geistigem Eigentum und sicherheitsrelevanter Informationen (Passwörter!) zur Folge haben. Deshalb ziehen es viele Systemadministratoren vor, eine zweite Verzeichnisstruktur für Skripte einzurichten, auf die nur durch das PHP-CGI zugegriffen werden kann. Diese werden dann stets interpretiert und nicht angezeigt. </simpara> <simpara> Auch wenn die Methode zum sichergestellten Verhindern einer Umleitung von Anfragen (wie im vorangegangenen Kapitel beschrieben) nicht verfügbar ist, ist es notwendig, ein doc_root für Skripte zusätzlich zum Web-Dokumentenverzeichnis einzurichten. </simpara> <simpara> Sie können das PHP-Skriptverzeichnis durch die Direktive <link linkend="ini.doc-root">doc_root</link> in der <link linkend="configuration.file">Konfigurationsdatei</link> festlegen, oder Sie setzen die Umgebungsvariable <envar>PHP_DOCUMENT_ROOT</envar>. Wenn sie gesetzt ist, wird die CGI-Version von PHP den Namen der zu öffnenden Datei stets aus <parameter>doc_root</parameter> und der Pfadinformation der Anfrage zusammensetzen, sodass man sicher sein kann, dass außerhalb dieses Verzeichnisses keine Skripte ausgeführt werden (außer <parameter>user_dir</parameter>, siehe unten). </simpara> <simpara> Eine weitere hier nützliche Option ist <link linkend="ini.user-dir">user_dir</link>. Wenn das <parameter>user_dir</parameter> nicht gesetzt ist, hat nur <parameter>doc_root</parameter> Einfluss auf die zu öffnende Datei. Der Aufruf einer URL wie <filename role="url">http://my.host/~user/doc.php</filename> hat nicht zum Ergebnis, dass eine Datei im Home-Verzeichnis des Benutzers geöffnet wird, sondern eine Datei namens <filename role="uri">~user/doc.php</filename> unterhalb des doc_root (Ja, ein Verzeichnisname, der mit einer Tilde anfängt [<literal>~</literal>]). </simpara> <simpara> Ist das user_dir beispielsweise auf <filename class="directory" role="dir">public_php</filename> gesetzt, wird eine Anfrage wie <filename role="url">http://my.host/~user/doc.php</filename> eine Datei namens <filename>doc.php</filename> im Verzeichnis <filename class="directory" role="dir">public_php</filename> im Heimatverzeichnis des Benutzers öffnen. Wenn das Heimatverzeichnis des Benutzers <filename class="directory" role="dir">/home/user</filename> ist, so ist die ausgeführte Datei <filename>/home/user/public_php/doc.php</filename>. </simpara> <simpara> Die <parameter>user_dir</parameter>-Expansion erfolgt ohne Berücksichtigung der <parameter>doc_root</parameter> Einstellung. So können Zugriffe auf die Dokumenten- und Benutzerverzeichnisse separat gesteuert werden. </simpara> </sect2> <sect2 id="security.cgi-bin.shell"> <title>Fall 4: PHP-Parser außerhalb des Webverzeichnisbaums</title> <para> Eine sehr sichere Sache ist es, das PHP-Parser-Binary irgendwo außerhalb des Webverzeichnisbaums zu platzieren, beispielsweise in <filename class="directory" role="dir">/usr/local/bin</filename>. Der einzige Nachteil dieses Verfahrens ist, dass eine Zeile ähnlich der folgenden: <informalexample> <programlisting> <![CDATA[ #!/usr/local/bin/php ]]> </programlisting> </informalexample> als erste Zeile in jeder Datei, die PHP-Tags enthält, stehen muss. Außerdem muss die Datei ausführbar sein. Ansonsten ist sie genauso zu behandeln wie ein beliebiges CGI-Script in Perl oder sh oder anderen gebräuchlichen Scriptsprachen, die den <literal>#!</literal> shell-escape-Mechanismus nutzen, um sich selbst aufzurufen. </para> <para> Damit PHP bei dieser Konfiguration die <envar>PATH_INFO</envar>- und <envar>PATH_TRANSLATED</envar>-Informationen korrekt auswertet, sollte der PHP-Parser mit der Option <link linkend="install.configure.enable-discard-path">--enable-discard-path</link> kompiliert werden. </para> </sect2> </sect1> <sect1 id="security.apache"> <title>Apache-Modul</title> <simpara> Wenn PHP als Apache-Modul eingesetzt wird, übernimmt es die Benutzerrechte des Apache (üblicherweise user "nobody"). Das hat verschiedene Auswirkungen auf Sicherheit und Authentifizierung, z.B.: Wenn Sie PHP zum Zugriff auf eine Datenbank benutzen, es sei denn diese Datenbank hat eine integrierte Zugriffskontrolle, müssen Sie dem Benutzer "nobody" Zugriffsrechte auf die Datenbank erteilen. Das heißt, dass ein böswilliges Skript auch Benutzerkennung und Passwort auf die Datenbank zugreifen, und sie verändern könnte. Es ist durchaus möglich, dass ein Web-Spider über die Webseite eines Datenbankadministrators stolpert, und alle Ihre Datenbanken löscht. Sie können sich dagegen mit Apache Authentifizierung schützen, oder ein eigenes Zugangsmodell unter Verwendung von LDAP, .htaccess Dateien, etc. entwerfen, und diesen Code als Teil Ihrer PHP Skripte einbinden. </simpara> <simpara> Es wurde festgestellt, dass wenn einmal die Sicherheitsmaßnahmen so weit eingerichtet sind dass dem PHP User (in diesem Fall ein Apache User) nur mehr ein geringes Risiko bleibt, PHP daran gehindert wird, virenverseuchte Dateien in das Benutzerverzeichnis zu schreiben. Oder vielleicht wurde es auch daran gehindert, auf Datenbanken zuzugreifen oder diese gar zu verändern. In gleicher Weise wird auch davor abgehalten, "gute" oder "bösartige" Dateien zu schreiben, oder "gute" bzw. "bösartige" Datenbanktransaktionen durchzuführen. </simpara> <simpara> Ein häufig gemachter Fehler in Punkto Sicherheit ist Apache Root-Rechte zu erteilen, oder die Möglichkeiten von Apache in einer anderen Weise auszuweiten. </simpara> <simpara> Die Ausweitung der Benutzerrechte für Apache auf root ist sehr gefährlich, und kann dem gesamten System schaden, denn mit sudo, chroot, oder anderwärtig als root zu arbeiten sollte niemand anders als den Sicherheitsprofis überlassen werden. </simpara> <simpara> Es gibt auch ein paar einfachere Lösungen. Mit <link linkend="ini.open-basedir">open_basedir()</link> können Sie kontrollieren, welche Verzeichnisse PHP benutzen darf oder nicht. Sie können auch einen Bereich nur für Apache einrichten, um alle webbasierten Aktivitäten auf nicht-Benutzer- bzw. nicht-System-Dateien einzuschränken. </simpara> </sect1> <sect1 id="security.filesystem"> <title>Dateisystem - Sicherheit</title> <simpara> PHP ist von den in den meisten Serversystemen implementierten Sicherheitseinstellungen hinsichtlich der Berechtigungen auf Datei- und Verzeichnisebene abhängig. Dies verleiht Ihnen Kontrolle darüber, welche Dateien in dem Dateisystem gelesen werden dürfen. Vorsicht ist bei weltweit lesbaren Dateien geboten um sicherzustellen, dass diese sicher von allen Usern mit Zugriff auf dieses Dateisystem (nur) gelesen werden können. </simpara> <simpara> Da PHP entwickelt wurde um Zugriffe auf das Dateisystem auf Benutzebene zu erlauben, ist es natürlich auch möglich ein PHP Skript zu schreiben das Ihnen erlaubt Systemdateien wie /etc/passwd zu lesen, Ethernetverbindungen zu modifizieren, enorme Druckaufträge zu senden, etc. Dies hat offensichtliche Implikationen, indem Sie sicherstellen müssen, dass alle von Ihnen zu lesenden bzw. zu schreibenden Dateien auch die richtigen sind. </simpara> <simpara> Stellen Sie sich folgendes Skript vor, in dem ein User zum Ausdruck bringt, dass gerne eine Datei in seinem Heimatverzeichnis löschen möchte. Dies geht von einer Situation aus, in der ein PHP Web-Interface regelmäßig zum Dateimanagement verwendet wird, und der Apache User ist auch berechtigt, in seinem Heimatverzeichnis Dateien zu löschen. </simpara> <para> <example> <title>Schlechte Variablenprüfung führt zu....</title> <programlisting role="php"> <![CDATA[ <?php // Löschen einer Datei aus dem Heimatverzeichnis des Users $username = $_POST['user_submitted_name']; $homedir = "/home/$username"; $file_to_delete = "$userfile"; unlink ("$homedir/$userfile"); echo "$file_to_delete wurde gelöscht!"; ?> ]]> </programlisting> </example> Da der Benutzername über ein User-Formular zu posten ist, kann jeder einen Benutzer- und Dateinamen von jemand anderen übertragen, und Dateien löschen. In diesem Fall empfiehlt es sich, eine andere Form der Authentifizierung zu verwenden. Stellen Sie sich vor was passieren würde, wenn die übertragenen Variablen "../etc/" und "passwd" beinhalten würden. Der Code würde dann effektiv lesen: <example> <title>... Ein Angriff auf das Dateisystem</title> <programlisting role="php"> <![CDATA[ <?php // löscht eine Datei irgendwo auf der Festplatte, wo der // Benutzer die nötigen Rechte besitzt. Wenn PHP root hat: $username = "../etc/"; $homedir = "/home/../etc/"; $file_to_delete = "passwd"; unlink ("/home/../etc/passwd"); echo "/home/../etc/passwd wurde gelöscht!"; ?> ]]> </programlisting> </example> Es gibt zwei wichtige Kriterien die Sie beachten sollten, um diese Dinge zu vermeiden: <itemizedlist> <listitem> <simpara> Erteilen Sie dem PHP Web-user (Binärdatei) nur eingeschränkte Rechte. </simpara> </listitem> <listitem> <simpara> Prüfen Sie alle übertragenen Variablen. </simpara> </listitem> </itemizedlist> Hier ist ein verbessertes Skript: <example> <title>Etwas sicherere Prüfung des Dateinamens</title> <programlisting role="php"> <![CDATA[ <?php // löscht eine Datei von der Festplatte, auf die // der PHP user Zugriff hat. $username = $_SERVER['REMOTE_USER']; // verwendet eine // Authentifizierungsmethode $homedir = "/home/$username"; $file_to_delete = basename("$userfile"); // den Pfad entfernen unlink ($homedir/$file_to_delete); $fp = fopen("/home/logging/filedelete.log","+a"); //logge die Löschung $logstring = "$username $homedir $file_to_delete"; fputs ($fp, $logstring); fclose($fp); echo "$file_to_delete wurde gelöscht!"; ?> ]]> </programlisting> </example> Auch dies nicht völlig makellos. Wenn Ihr Authentifizierungssystem Benutzern erlauben sollte, deren eigene Logins zu kreieren, und ein Benutzer wählt den Login "../etc", ist das System wieder aufgedeckt. Aus diesem Grund ziehen Sie es vielleicht vor, einen besseren Check zu schreiben: <example> <title>Sicherere Dateinamensprüfung</title> <programlisting role="php"> <![CDATA[ <?php $username = $HTTP_SERVER_VARS['REMOTE_USER']; // verwendet eine // Authentifizierungsmethode $homedir = "/home/$username"; if (!ereg('^[^./][^/]*$', $userfile)) die('bad filename'); // "DIE", gehen Sie nicht weiter if (!ereg('^[^./][^/]*$', $username)) die('bad username'); // "DIE", gehen Sie nicht weiter //etc... ?> ]]> </programlisting> </example> </para> <para> Abhängig vom Betriebssystem gibt es eine große Anzahl Dateien mit der Sie sich befassen sollten, inklusive Einträge für Geräte (/dev/ oder com1), Konfigurationsdateien (/etc/ Dateien und die .ini Dateien), gut bekannte Verzeichnisse (/home/, My Documents), etc. Aus diesem Grund ist es gewöhnlich einfacher eine Vorgangsweise einzuführen, bei der außer den von Ihnen explizit erlaubten Dingen alles verboten ist. </para> </sect1> <sect1 id="security.database"> <title>Datenbank - Sicherheit</title> <simpara> Heutzutage sind Datenbanken die Hauptkomponenten jeder Webbasierten Applikation, aufgrund welcher Websites verschiedene dynamische Inhalte anbieten können. Nachdem heikle oder geheime Informationen in solch einer Datenbank gespeichert werden können, sollten Sie deren Schutz ernsthaft bedenken. </simpara> <simpara> Um Informationen zu bekommen oder zu speichern, müssen Sie eine legitime Abfrage senden, das Ergebnis holen, und die Verbindung schließen. Heutzutage ist die allgemein verwendete Abfragesprache für solche Interaktionen die Structured Query Language (SQL). Sehen Sie, wie sich ein Angreifer <link linkend="security.database.sql-injection">an einer SQL Abfrage zu schaffen machen</link> kann. </simpara> <simpara> Sie werden merken, dass PHP Ihre Datenbank alleine nicht schützen kann. Die folgenden Abschnitte sind eine Einführung in die Grundlagen, wie man innerhalb von PHP Skripten auf Datenbanken zugreift und diese manipuliert. </simpara> <simpara> Denken Sie an diese einfache Regel: tief gestaffelte Verteidigung. Je mehr Platz Sie den Maßnahmen zum Schutz Ihrer Datenbank geben, desto geringer ist die Wahrscheinlichkeit, dass ein Angreifer Erfolg hat, und gespeicherte Geheiminformationen aufdeckt oder missbraucht. Gutes Design des Datenbankschemas, und die Applikation wird mit Ihren größten Befürchtungen fertig. </simpara> <sect2 id="security.database.design"> <title>Datenbanken designen</title> <simpara> Der Erste Schritt liegt immer im Erstellen der Datenbank, außer Sie wollen eine bereits existierende Dritter verwenden. Ist eine Datenbank erstellt, ist sie einem Eigentümer zugewiesen, welcher das Kommando zum Erstellen ausgeführt hat. Gewöhnlich kann nur der Eigentümer (oder ein Superuser) alles mit den Objekten in dieser Datenbank machen, und um anderen Benutzern die Verwendung zu erlauben, müssen Rechte vergeben werden. </simpara> <simpara> Applikationen sollten sich mit der Datenbank nie als deren Eigentümer oder als ein Superuser verbinden, da diese Benutzer jede gewollte Abfrage ausführen können, um z.B. das Schema zu modifizieren (z.B. Tabellen löschen) oder den gesamten Inhalt löschen. </simpara> <simpara> Sie können verschiedene Datenbanknutzer mit sehr limitierten Rechten auf Datenbankobjekte für jeden Aspekt Ihrer Applikation anlegen. Nur die wirklich benötigten Rechte sollten gewährt werden, und vermeiden Sie, dass der gleiche Benutzer in verschiedenen Anwendungsfällen mit der Datenbank interagieren kann. Das heißt, dass Eindringlinge, welche unter Verwendung einer dieser Referenzen Zugriff auf Ihre Datenbank erlangt haben, nur so viele Änderungen durchführen können, wie es Ihre Applikation kann. </simpara> <simpara> Implementieren Sie nicht alle Geschäftslogik in die Webapplikation (z.B. Ihr Skript), sondern tun Sie das im Datenbankschema unter Verwendung von Sichten, Triggern, oder Regeln. Wenn sich das System entwickelt, werden neu zu öffnende Ports zu der Datenbank vorgesehen, und Sie müssen die Logik in jedem Datenbank-Client neu implementieren. Überdies können Trigger verwendet werden, um transparent und automatisch mit Feldern umzugehen, welche beim debuggen Ihrer Applikation oder beim Zurückverfolgen von Transaktionen oft einen Einblick gewähren. </simpara> </sect2> <sect2 id="security.database.connection"> <title>Zur Datenbank verbinden</title> <simpara> Vielleicht wollen Sie die Verbindungen über SSL herstellen, um die Client/Server Kommunikation für eine erhöhte Sicherheit zu verschlüsseln, oder aber auch ssh verwenden, um die Netzwerkverbindung zwischen den Clients und dem Datenbankserver zu verschlüsseln. Ist eines davon realisiert, wird ein Monitoring Ihres Verkehrs und das Erlangen von Informationen zu harter Arbeit. </simpara> <!--simpara> If your database server has native SSL support, consider to use <link linkend="ref.openssl">OpenSSL functions</link> in communication between PHP and database via SSL. </simpara--> </sect2> <sect2 id="security.database.storage"> <title>Verschlüsseltes Speichermodell</title> <simpara> SSL/SSH schützt zwar die gerade auf dem Weg befindlichen Daten vom Client zum Server, jedoch nicht die dauernd in einer Datenbank gespeicherten Daten. SSL ist ein "auf-der-Leitung" Protokoll. </simpara> <simpara> Hat ein Angreifer direkten Zugriff auf Ihre Datenbank (den Webserver umgehend), können die gespeicherten heiklen Daten aufgedeckt oder zweckentfremdet werden, außer wenn die Information von der Datenbank selbst geschützt ist. Die Daten zu verschlüsseln ist ein guter Weg, diese Gefahr zu mildern, doch bieten nur wenige Datenbanken diese Art der Verschlüsselung von Daten. </simpara> <simpara> Der einfachste Weg, dieses Problem zu umgehen ist, erst einmal Ihr eigenes Verschlüsselungspaket zu erstellen, und dieses dann in Ihren PHP Skripten zu nutzen. PHP kann Ihnen in diesem Fall mit seinen verschiedenen Erweiterungen helfen, wie z.B. <link linkend="ref.mcrypt">Mcrypt</link> and <link linkend="ref.mhash">Mhash</link>, welche eine große Auswahl an Verschlüsselungsalgorhythmen abdecken. Das Skript verschlüsselt die Daten vor dem Speichern, und entschlüsselt diese wieder beim Erhalt. Siehe die Verweise für weitere Beispiele, wie Verschlüsselung arbeitet. </simpara> <simpara> Im Fall von wirklich versteckten Daten, wenn deren unverfälschte Repräsentation nicht nötig ist (z.B. keine Anzeige), ist hashing ebenfalls überlegenswert. Das bekannte Beispiel für das Hashing ist das Speichern des MD5 hash eines Passwortes in einer Datenbank, anstatt des Passwortes selbst. Siehe auch <function>crypt</function> und <function>md5</function>. </simpara> <example> <title>Verwenden eines hashed Passwortfeldes</title> <programlisting role="php"> <![CDATA[ // Speichern des Passwort hash $query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');", addslashes($username), md5($password)); $result = pg_exec($connection, $query); // Afragen, ob der User das richtige Passwort übermittelt hat $query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';", addslashes($username), md5($password)); $result = pg_exec($connection, $query); if (pg_numrows($result) > 0) { echo "Welcome, $username!"; } else { echo "Authentication failed for $username."; } ]]> </programlisting> </example> </sect2> <sect2 id="security.database.sql-injection"> <title>SQL Injection</title> <simpara> Viele Entwickler sind sich nicht bewusst, wie man sich an SQL Abfragen zu schaffen machen kann und nehmen an, dass eine SQL Abfrage ein vertrauenswürdiges Kommando ist. Das heißt, dass SQL Abfragen Zugriffskontrollen hinters Licht führen, und dadurch Standard Authentifizierungs- und Authorisationschecks umgehen können, und manchmal können SQL Abfragen sogar Zugriff zu Kommandos auf Betriebssystemebene erlauben. </simpara> <simpara> Direkt SQL Command Injection ist eine Technik, wo ein Angreifer SQL Kommandos erstellt oder existierende verändert, um versteckte Daten sichtbar zu machen, wertvolle Daten zu überschreiben, oder sogar gefährliche Kommandos auf Systemebene des Datenbank-Hosts auszuführen. Dies wird durch die Applikation erreicht, welche den Input des Benutzers mit statischen Parametern kombiniert, um eine SQL Abfrage zu erstellen. Die folgenden Beispiele basieren - leider - auf wahren Begebenheiten. </simpara> <para> Dank dem Mangel an Input Validierungen, und dem Verbinden zum Datenbankserver als ein Superuser oder jemand der Benutzer anlegen kann, kann ein Angreifer einen Superuser in Ihrer Datenbank anlegen. <example> <title> Die Ergebnisliste in mehrere Seiten aufsplitten ... und Superuser anlegen (PostgreSQL and MySQL) </title> <programlisting role="php"> <![CDATA[ $offset = argv[0]; // Vorsicht, keine Validierung des Input ! $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; // mit PostgreSQL $result = pg_exec($conn, $query); // mit MySQL $result = mysql_query($query); ]]> </programlisting> </example> Normale Benutzer klicken auf die 'nächste' bzw. 'vorige' Links, wo <varname>$offset</varname> in der URL enthalten ist. Das Skript erwartet, dass die ankommende <varname>$offset</varname> einen Dezimalwert enthält. Ganz gleich, jemand versucht einzubrechen, indem er das folgende in einer <function>urlencode</function>'d Form an die URL anhängt <informalexample> <programlisting> <![CDATA[ // Im Fall von PostgreSQL 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // Im Fall von MySQL 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; ]]> </programlisting> </informalexample> Wenn es passiert ist, würde ihm das Skript einen Zugriff als Superuser präsentieren. Beachten Sie, dass <literal>0;</literal> ein gültiges Offset zur ursprünglichen Abfrage liefert, und sie beendet. </para> <note> <para> Es ist eine übliche Technik, den SQL Parser mittels dem Kommentarzeichen in SQL <literal>--</literal> zu zwingen, den Rest der vom Entwickler geschriebenen Abfrage zu ignorieren. </para> </note> <para> Ein gangbarer Weg um Passwörter zu finden ist, Ihre Seiten mit den Suchergebnissen hinters Licht zu führen. Der Angreifer braucht nur zu probieren, ob irgendeine übertragene Variable, die in dem SQL Statement verwendet wird, nicht richtig gehandhabt wird. Diese Filter können gewöhnlich in einer vorausgehenden Form gesetzt werden, indem <literal>WHERE, ORDER BY, LIMIT</literal> und <literal>OFFSET</literal> Klauseln in <literal>SELECT</literal> Statements umgebaut werden. Wenn Ihre Datenbank das <literal>UNION</literal> Konstrukt unterstützt, kann der Angreifer versuchen, eine komplette Abfrage an das Original anzuhängen, um Paßwörter aus einer willkürlichen Tabelle aufzulisten. Die Verwendung von verschlüsselten Passwortfeldern wird ausdrücklich empfohlen. <example> <title> Artikel auflisten ... und ein paar Passwörter (irgendein Datenbankserver) </title> <programlisting role="php"> <![CDATA[ $query = "SELECT id, name, inserted, size FROM products WHERE size = '$size' ORDER BY $order LIMIT $limit, $offset;"; $result = odbc_exec($conn, $query); ]]> </programlisting> </example> Der statische Teil der Abfrage kann mit einem anderen <literal>SELECT</literal> Statement kombiniert werden, welches alle Passwörter preisgibt <informalexample> <programlisting> <![CDATA[ ' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; -- ]]> </programlisting> </informalexample> Wenn diese Abfrage (mit dem <literal>'</literal> und <literal>--</literal>) einer der in <varname>$query</varname> verwendeten Variablen zugewiesen würde, wäre das "Abfragebiest" erwacht. </para> <para> SQL UPDATEs sind ebenfalls ein Anlass, Ihre Datenbank anzugreifen. Diese Abfragen sind auch durch das Ändern und Anhängen einer komplett neuen Abfrage gefährdet. Aber der Angreifer könnte auch mit der <literal>SET</literal> Klausel herumspielen. In diesem Fall muss eine Schemainformation vorhanden sein, um die Abfrage erfolgreich manipulieren zu können. Diese kann durch Untersuchen der Variablennamen im Formular, oder simpel mittels brute force gesammelt werden. Es gibt nicht so viele Namenskonventionen für Felder, welche Passwörter oder Benutzernamen speichern. <example> <title> Vom Zurücksetzen eines Passwortes ... zum Erlangen von mehr Rechten (irgendein Datenbankserver) </title> <programlisting role="php"> <![CDATA[ $query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';"; ]]> </programlisting> </example> Aber ein böswilliger Benutzer übermittelt den Wert <literal>' or uid like'%admin%'; --</literal> zu <varname>$uid</varname>, um das Administrator Passwort zu ändern, oder setzt einfach <varname>$pwd</varname> auf <literal>"hehehe', admin='yes', trusted=100 "</literal> (mit dem hinteren Leerzeichen), um mehr Rechte zu erhalten. Dann wird die Abfrage verdreht: <informalexample> <programlisting role="php"> <![CDATA[ // $uid == ' or uid like'%admin%'; -- $query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --"; // $pwd == "hehehe', admin='yes', trusted=100 " $query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;" ]]> </programlisting> </informalexample> </para> <para> Ein furchterregendes Beispiel, wie der Zugriff auf Kommandos auf Betriebssystemebene bei manchen Datenbankservern erfolgen kann. <example> <title>Angriff auf das Betriebssystem des Datenbank Hosts (MSSQL Server)</title> <programlisting role="php"> <![CDATA[ $query = "SELECT * FROM products WHERE id LIKE '%$prod%'"; $result = mssql_query($query); ]]> </programlisting> </example> Wenn ein Angreifer den Wert <literal>a%' exec master..xp_cmdshell 'net user test testpass /ADD' --</literal> zu <varname>$prod</varname> überträgt, wird <varname>$query</varname> zu: <informalexample> <programlisting role="php"> <![CDATA[ $query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--"; $result = mssql_query($query); ]]> </programlisting> </informalexample> Der MSSQL Server führt die SQL Statements in dem Batch aus, inklusive einem Kommando zum Anlegen eines neuen Benutzers in der Datenbank Accounts. Würde diese Applikation als <literal>sa</literal> und der MSSQLSERVER Service mit genügend Rechten laufen, hätte der Angreifer nun ein Konto, mit welchem er Zugriff auf diese Maschine hätte. </para> <note> <para> Manche der obigen Beispiele sind an einen spezifischen Datenbankserver gebunden. Das heißt jedoch nicht, dass nicht ein ähnlicher Angriff auf andere Produkte möglich wäre. Ihr Datenbankserver könnte auf andere Weise genauso verwundbar sein. </para> </note> <sect3 id="security.database.avoiding"> <title>Techniken zur Vermeidung</title> <simpara> Sie könnten sich nun darauf berufen, dass der Angreifer in den meisten Beispielen ein Stück Information über das Datenbankschema haben muss. Sie haben recht, aber Sie wissen nie, wann und wie es genommen werden kann, und wenn es passiert, kann Ihre Datenbank entblößt werden. Wenn Sie ein Open Source, oder öffentlich verfügbares Paket zur Handhabung von Datenbanken verwenden, welches vielleicht zu einem Content Management System oder Forum gehört, können Eindringlinge leicht eine Kopie eines Stücks Ihres Codes erstellen. Es kann auch ein Sicherheitsrisiko sein, wenn es sich um ein schlecht designtes Paket handelt. </simpara> <simpara> Diese Angriffe basieren hauptsächlich auf dem Ausnutzen des Codes, welcher ohne Bedenken auf die Sicherheit geschrieben wurde. Vertrauen Sie nie auf irgendeine Art von Input, speziell wenn er von der Clientseite kommt, selbst wenn er von einer Auswahlbox, einem versteckten Eingabefeld, oder einem Cookie kommt. Das erste Beispiel zeigt, dass solch eine untadelige Abfrage ein Disaster anrichten kann. </simpara> <itemizedlist> <listitem> <simpara> Stellen Sie nie als Superuser oder Owner einer Datenbank eine Verbindung zur Datenbank her. Verwenden Sie immer speziell angelegte Benutzer mit sehr limitierten Rechten. </simpara> </listitem> <listitem> <simpara> Prüfen Sie, ob der gegebene Input dem erwarteten Datentyp entspricht. PHP bietet eine große Auswahl an Funktionen zum Validieren des Input, von den einfachsten unter <link linkend="ref.variables">Variablenfunktionen</link> und <link linkend="ref.ctype">Character Type Functions</link> (z.B. <function>is_numeric</function> bzw. <function>ctype_digit</function>), bis hin zu den <link linkend="ref.pcre">Perl kompatiblen Regulären Ausdrücken</link>. </simpara> </listitem> <listitem> <para> Wenn die Applikation numerischen Input erwartet, erwägen Sie die Prüfung der Daten mit <function>is_numeric</function>, oder die Änderung des Typs mit <function>settype</function>, oder verwenden Sie die numerische Repräsentation mittels <function>sprintf</function>. <example> <title>Ein sicherer Weg, eine Abfrage zu erstellen</title> <programlisting role="php"> <![CDATA[ settype($offset, 'integer'); $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; // Beachten Sie %d im Formatstring, %s zu verwenden wäre sinnlos $query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;", $offset); ]]> </programlisting> </example> </para> </listitem> <listitem> <simpara> Escapen Sie jeden nicht numerischen Input, welcher zur Datenbank weitergereicht werden soll mit <function>addslashes</function> oder <function>addcslashes</function>. Siehe auch <link linkend="security.database.storage">das erste Beispiel</link>. Wie dieses Beispiel zeigt, sind in den statischen Teil der Abfrage eingebrachten Escapes nicht genug, und können leicht gehacked werden. </simpara> </listitem> <listitem> <simpara> Geben Sie keinerlei datenbankspezifische Informationen aus, speziell über das Schema, egal wie (auf ehrliche oder unehrliche Weise). Siehe auch <link linkend="security.errors">Fehlerbehandlung</link> und <link linkend="ref.errorfunc">Error Handling and Logging Functions</link>. </simpara> </listitem> <listitem> <simpara> Sie können stored procedures und vorher definierte Cursor verwenden, um den Datenzugriff zu abstrahieren, sodass Benutzer nicht direkt auf Tabellen oder Views zugreifen, aber diese Lösung hat andere Auswirkungen. </simpara> </listitem> </itemizedlist> <simpara> Abgesehen davon profitieren Sie von einer Protokollierung der Abfragen entweder in Ihrem Skript, oder durch die Datenbank selbst, wenn es hilft. Klar, die Protokollierung kann nicht irgendeinen schädlichen Versuch verhindern, aber es kann helfen herauszufinden, welche Applikation umgangen wurde. Das Log ist durch die in ihm enthaltene Information nützlich, und je mehr Details es enthält, desto besser ist es im Allgemeinen. </simpara> </sect3> </sect2> </sect1> <sect1 id="security.errors"> <title>Fehlerbehandlung</title> <para> PHP Security hat zwei Seiten der Fehlerbehandlung. Eine ist für die Erhöhung der Sicherheit vorteilhaft, die andere ist schädlich. </para> <para> Eine Standard-Angriffstaktik beinhaltet die Erstellung eines Profils des anzugreifenden Systems, indem die aufgrund der Einspeisung von unzulässigen Daten zurückgegebenen Fehlermeldungen anhand deren Art und des Kontextes ausgewertet werden. Wenn z.B. ein Angreifer Informationen über eine auf einem eingesendeten Formular basierte Seite zusammengetragen hat, kann er versuchen, Variablen zu überschreiben bzw. zu modifizieren: <example> <title>Variablen mit einer eigenen HTML Seite angreifen</title> <programlisting role="php"> <![CDATA[ <form method="post" action="attacktarget?username=badfoo&password=badfoo"> <input type="hidden" name="username" value="badfoo"> <input type="hidden" name="password" value="badfoo"> </form> ]]> </programlisting> </example> </para> <para> Die normalerweise zurückgegebenen PHP Fehler können für den Entwickler hilfreich sein, wenn dieser ein Skript debuggen möchte, Hinweise auf eine nicht korrekt arbeitende Funktion oder Datei, oder die PHP Datei und die Zeilennummer des aufgetretenen Fehlers anzeigen lassen möchte. Das sind alles Informationen, die ausgenutzt werden können. Es ist für einen PHP Entwickler nicht unüblich, <function>show_source</function>, <function>highlight_string</function>, oder <function>highlight_file</function> zur Fehlersuche zu verwenden, jedoch kann dies in einem lebenden System auch versteckte Variablen, ungeprüfte Syntax und andere gefährliche Informationen aufdecken. Speziell gefährlich ist es, Code von bekannten Quellen mit integrierten Debugging Handlern auszuführen, oder weit verbreitete Debuggingtechniken zu verwenden. Wenn ein Angreifer die von Ihnen benutzte generelle Technik herausfindet, kann er versuchen, mit Brute-Force Ihre Seite zu knacken, indem er verschiedene allgemein gebräuchliche Debug Strings sendet: <example> <title>Ausnutzen von gebräuchlichen Debugging Variablen</title> <programlisting role="php"> <![CDATA[ <form method="post" action="attacktarget?errors=Y&amp;showerrors=1"&debug=1"> <input type="hidden" name="errors" value="Y"> <input type="hidden" name="showerrors" value="1"> <input type="hidden" name="debug" value="1"> </form> ]]> </programlisting> </example> </para> <para> Ungeachtet der Fehlerbehandlungsmethode führt die Möglichkeit ein System nach Fehlermeldungen sondieren dazu, dass einem Angreifer mehr Informationen geboten werden. </para> <para> Zum Beispiel weist schon alleine der Stil einer Fehlermeldung darauf hin, dass auf einem System PHP läuft. Wenn der Angreifer auf eine .html Seite kommt und untersuchen möchte welches System im Hintergrund läuft (um nach bekannten Systemschwächen zu suchen), könnte dieser mittels der Einspeisung von falschen Daten herausfinden, dass ein System mit PHP aufgebaut ist. </para> <para> Ein Fehler einer Funktion gibt Aufschluss darüber, ob ein System eine bestimmte Datenbankapplikation benutzt, oder gibt Hinweise darauf, wie eine Webseite programmiert bzw. entworfen wurde. Dies erlaubt eine tiefere Überprüfung von offenen Datenbank-Ports, oder die Suche nach spezifischen Bugs bzw. Schwächen einer Webseite. Mit der Einspeisung von falschen Daten kann ein Angreifer z.B. die Reihenfolge der Authentifizierung in einem Skript bestimmen (anhand der Zeilennummern in den Fehlermeldungen), wie auch durch "Herumstochern" Missbrauchsmöglichkeiten an verschiedenen Stellen im Script herausfinden. </para> <para> Eine Fehlermeldung des Dateisystems oder eines generellen PHP-Errors welche Rechte der Server hat, wie auch die Struktur und Organisation der Dateien auf dem Webserver. Vom Entwickler geschriebene Fehlermeldungen können das Problem verschlimmern, bis hin zum Preisgeben von zuvor "versteckten" Informationen. </para> <para> Es gibt drei bedeutende Lösungen zu diesem Thema. Die erste ist, alle Funktionen zu überprüfen und zu versuchen, die Menge an Fehlermeldungen zu ersetzen. Die zweite ist, die Ausgabe von Fehlermeldungen am laufenden Code generell zu deaktivieren. Die dritte ist, sich unter Verwendung der PHP Funktionen zur Fehlerbehandlung seinen eigenen Error-handler zu schreiben. Abhängig von Ihrer Sicherheitspolitik könnte jede der drei Lösungen für Sie geeignet sein. </para> <para> Ein Weg, diesen Punkt vorzeitig zu behandeln ist, das PHP eigene <function>error_reporting</function> zu benutzen, um Ihren Code sicherer zu gestalten und möglicherweise gefährliche Nutzungen von Variablen zu entdecken. Wenn Sie Ihren Code noch vor dem Einsatz mit E_ALL testen, können Bereiche entdecken, in denen Ihre Variablen eventuell für Verseuchung oder andere Modifikationen offen sind. Sind Sie bereit zum Einsatz, können Sie Ihren Code mit E_NONE vor Sondierungen schützen. <example> <title>Gefährliche Variablen mit E_ALL finden</title> <programlisting role="php"> <![CDATA[ <?php if ($username) { // Vor Verwendung nicht initialisiert oder geprüft $good_login = 1; } if ($good_login == 1) { // Wenn der obige Test fehlschlägt, ist vor der // Verwendung nicht initialisiert oder geprüft fpassthru ("/highly/sensitive/data/index.html"); } ?> ]]> </programlisting> </example> </para> </sect1> <sect1 id="security.registerglobals"> <title>Verwendung von Register Globals</title> <para> Ein Feature von PHP zur Erhöhung der Sicherheit ist die Konfiguration von PHP mit <link linkend="ini.register-globals">register_globals</link> = off. Mit Deaktivierung der Möglichkeit, irgendeine vom Benutzer übertragenen Variable in den PHP Code zu injizieren, können Sie die Anzahl "vergifteter" Variablen reduzieren, welche ein potentieller Angreifer zufügen könnte. Dieser benötigt mehr Zeit, um sich Übermittlungen auszudenken, und Ihre internen Variablen sind effektiv von den übergebenen Benutzervariablen isoliert. </para> <para> Während dies den benötigten Aufwand mit PHP zu arbeiten leicht erhöht ist dargelegt, dass die Vorteile gegenüber dem Aufwand klar überwiegen. <example> <title>Mit register_globals=on arbeiten</title> <programlisting role="php"> <![CDATA[ <?php if ($username) { // kann vom User mit get/post/cookies übermittelt werden $good_login = 1; } if ($good_login == 1) { // kann vom User mit get/post/cookies übermittelt werden fpassthru ("/highly/sensitive/data/index.html"); } ?> ]]> </programlisting> </example> <example> <title>Mit register_globals = off arbeiten</title> <programlisting role="php"> <![CDATA[ <?php if($_COOKIE['username']){ // kann nur von einem Cookie kommen $good_login = 1; fpassthru ("/highly/sensitive/data/index.html"); } ?> ]]> </programlisting> </example> Dies weise genutzt ist es auch möglich, präventive Messungen durchzuführen, um bei versuchten Vorstößen zu warnen. Wenn Sie im Voraus wissen, woher eine Variable kommen soll, können Sie prüfen, ob die übermittelten Daten nicht einen unpassenden Weg genommen haben. Obwohl dies nicht garantiert, dass Daten nicht nur ausgedacht sind, erfordert es von einem Angreifer, auch den richtigen Weg zu finden. <example> <title>Entdecken einfacher Manipulationen von Variablen</title> <programlisting role="php"> <![CDATA[ <?php if ($_COOKIE['username'] && !$_POST['username'] && !$_GET['username'] ) { // Durchführen anderer Checks, ob der Benutzername gültig ist... $good_login = 1; fpassthru ("/highly/sensitive/data/index.html"); } else { mail("admin@example.com", "Possible breakin attempt", $_SERVER['REMOTE_ADDR']); echo "Security violation, admin has been alerted."; exit; } ?> ]]> </programlisting> </example> Natürlich bedeutet ein einfaches Deaktivieren von register_globals nicht, dass Ihr Code nun automatisch sicher ist. Jeder Teil mit Daten sollte auch auf andere Arten geprüft werden. </para> </sect1> <sect1 id="security.variables"> <title>Vom Nutzer übermittelte Daten</title> <para> In vielen PHP Programmen liegt die größte Schwäche nicht an der Sprache selbst, sondern bloß an Code, der nicht mit dem nötigen Augenmerk auf die Sicherheit geschrieben wurde. Deshalb sollten Sie sich immer Zeit nehmen, die Implikationen eines gegebenen Codestücks zu bedenken, um einen möglichen Schaden durch eine eventuell unerwartete übergebene Variable festzustellen. <example> <title>Gefährliche Verwendung von Variablen</title> <programlisting role="php"> <![CDATA[ <?php // lösche eine Datei aus dem Benutzer-Verzeichnis... // oder vielleicht dem eines anderen Benutzers? unlink ($evil_var); // Schreibe die Log-Information von deren Zugriff... // oder vielleicht einen /etc/passwd Eintrag? fputs ($fp, $evil_var); // Führe etwas triviales aus... oder rm -rf *? system ($evil_var); exec ($evil_var); ?> ]]> </programlisting> </example> Sie sollten Ihren Code immer sorgfältig kontrollieren, um eine sorgfältige Prüfung irgendwelcher von einem Web-Browser übertragenen Variablen sicherzustellen, und sich selbst folgende Fragen stellen: <itemizedlist> <listitem> <simpara> Wird sich dieses Skript nur auf die vorgesehenen Dateien auswirken? </simpara> </listitem> <listitem> <simpara> Kann auf ungewöhnliche oder unerwünschte Daten reagiert werden? </simpara> </listitem> <listitem> <simpara> Kann dieses Skript auf nicht vorgesehene Art genutzt werden? </simpara> </listitem> <listitem> <simpara> Kann dies in Verbindung mit anderen Skripten in einer negativen Art benutzt werden? </simpara> </listitem> <listitem> <simpara> Werden alle Transaktionen ausreichend geloggt? </simpara> </listitem> </itemizedlist> Wenn Sie sich diese Fragen anstatt danach schon während dem Schreiben des Skriptes stellen, ersparen Sie sich das unangenehme Umschreiben, wenn eine Erhöhung der Sicherheit erforderlich wird. Mit dieser Art zu denken werden Sie die Sicherheit des Systems zwar nicht garantieren, aber sie können helfen, sie zu erhöhen. </para> <para> Sie könnten auch Überlegungen anstellen, ob Sie nicht register_globals, magic_quotes, oder andere bequeme Einstellungen abschalten, welche Sie in Punkto Gültigkeit, Herkunft, oder Inhalt einer gegebenen Variable durcheinanderbringen könnten. Mit PHP im error_reporting(E_ALL) Modus zu arbeiten kann auch helfen, Sie vor Variablen zu warnen, welche benutzt werden, bevor sie geprüft oder initialisiert wurden (so können Sie verhindern, dass mit ungewöhnlichen Daten gearbeitet wird). </para> </sect1> <sect1 id="security.hiding"> <title>Verstecken von PHP</title> <para> Generell ist Sicherheit durch Unklarheit eine der schwächsten Formen von Sicherheit. Aber in manchen Fällen ist ein klein Wenig mehr an extra Sicherheit wünschenswert. </para> <para> Ein paar einfache Techniken helfen PHP zu Verstecken, um einen nach Schwächen in Ihrem System suchenden Angreifer möglicherweise langsamer zu machen. Wenn Sie in Ihrer &php.ini; expose_php = off setzen, reduzieren Sie damit die ihm zur Verfügung stehenden Informationen. </para> <para> Eine andere Taktik ist, den Webserver wie z.B. Apache entweder mittels einer .htaccess Direktive oder in der Apache Konfigurationsdatei selbst so konfigurieren, dass dieser verschiedene Dateitypen durch PHP parst. So können Sie irreführende Dateierweiterungen verwenden: <example> <title>PHP als andere Sprache ausgeben</title> <programlisting role="apache-conf"> <![CDATA[ # Lasse PHP Code wie andere Arten von Code aussehen AddType application/x-httpd-php .asp .py .pl ]]> </programlisting> </example> Oder komplett unklar machen: <example> <title>Verwenden von unbekannten Typen für PHP Dateierweiterungen</title> <programlisting role="apache-conf"> <![CDATA[ # Lasse PHP Code wie unbekannte Typen aussehen AddType application/x-httpd-php .bop .foo .133t ]]> </programlisting> </example> Oder verstecken Sie ihn als html Code, was einen leichten Performanceverlust bedeutet, da alle html Dateien durch die PHP Engine geparst werden: <example> <title>Verwenden von html Typen für PHP Dateierweiterungen</title> <programlisting role="apache-conf"> <![CDATA[ # Lasse PHP code wie html aussehen AddType application/x-httpd-php .htm .html ]]> </programlisting> </example> Um dies effektiv arbeiten zu lassen, müssen Sie Ihre PHP Dateien nach den obigen Dateierweiterungen umbenennen. Während dies eine Form der Sicherheit durch Verhüllung ist, ist es ein kleines präventives Maß mit ein paar Nachteilen. </para> </sect1> <sect1 id="security.current"> <title>Aktuell bleiben</title> <simpara> PHP wird, wie jedes andere große System auch, ständig geprüft und weiterentwickelt. Jede neue Version wird oft sowohl große, als auch kleine Änderungen enthalten, welche die Gesamtsicherheit und Stabilität des Systems betreffenden Punkte (z.B. Sicherheit, Konfiguration) verbessern bzw. vorhandene Mängel beheben. </simpara> <simpara> Wie auch bei anderen Scriptsprachen und Programmen auf Systemebene ist der beste Ansatz ein oftmaliges Update, und sich laufend über die letzten Versionen und deren Änderungen zu informieren. </simpara> </sect1> </chapter> <!-- Keep this comment at the end of the file Local variables: mode: sgml sgml-omittag:t sgml-shorttag:t sgml-minimize-attributes:nil sgml-always-quote-attributes:t sgml-indent-step:1 sgml-indent-data:t indent-tabs-mode:nil sgml-parent-document:nil sgml-default-dtd-file:"../../manual.ced" sgml-exposed-tags:nil sgml-local-catalogs:nil sgml-local-ecat-files:nil End: vim600: syn=xml fen fdm=syntax fdl=2 si vim: et tw=78 syn=sgml vi: ts=1 sw=1 -->
« previous php.doc.de (#762) next »