Thema Sicherheit: mysql_real_escape_string() & magic_quotes_gpc

Don Stefano

Erfahrenes Mitglied
Hallo zusammen,

da ich eh gerade meine Datenbankkommunikation umprogrammiere (der Probleme halber weg von der Objektorientierung) würde ich sie auch gern sicher machen und vor SQL Injektionen schützen.

Dies geschieht ja mit der Funktion mysql_real_escape_string() z.B. folgendermaßen:

PHP:
$query = " 
         		UPDATE `tabelle` 
    		 SET `name`	 = '".mysql_real_escape_string($name)."',
        `geschlecht`	 = '".mysql_real_escape_string($geschlecht)."', ...
anstatt

PHP:
 $query = ("update tabelle SET name='$name', geschlecht=$geschlecht",...
Meine Frage dazu. Ist dies für sämtliche Variablen notwendig, mit denen in den Skripten gearbetet wird, und die irgendwie mit der Datenbank in Verbindung stehen ?

Und dann habe ich unter dem angegebenen Link gelesen: "Falls Sie magic_quotes_gpc aktiviert haben und mit Daten aus Benutzereingaben arbeiten, müssen Sie vorher Ihre Daten mit stripslashes() behandeln."

Auf meinem Webserver ist magic_Quotes_gpc aktiviert und zwar nur dieses magic_quotes.

So wie ich das verstehe muss ich also stripslashes() bei jeder Variable verwenden, sie ich mit mysql_real_escape_string() "maskiere". Ist das wirklich notwendig?

Wie sähe dann der angepasste Code aus, den ich oben abgebildet habe? Folgendermaßen ?

PHP:
$query = " 
          		UPDATE `tabelle` 
 		 SET `name`	 = '".mysql_real_escape_string(stripslashes($name))."',
         `geschlecht`	 = '".mysql_real_escape_string(stripslashes($geschlecht))."', ...

Ist das so korrekt?
Was gibts es zu dem Thema noch zu wissen ? Oder bin ich dann damit vor SQL Injektionen sicher?

Außerdem würde mich mal folgendes interessieren. Ich habe eine Datei mit meinen Datanbankdaten, die zum DB-Aufbau immer includet wird. Ist es nicht unsicher, wenn die einfach so auf dem Server liegt, und kann man die Daten, die sie enthält nicht auch irgendwie den "laufenden Skripten" entnehmen (in böser Absicht)?


Danke für Eure Ratschläge und Hinweise
 
...Verwenden Sie daher die Funktion addslashes() nicht, wenn Strings bereits durch magic_quotes_gpc escaped wurden, ansonsten erhalten Sie doppeltes Escaping. Um herauszufinden, ob der Parameter aktiviert ist, verwenden Sie am Einfachsten die Funktion get_magic_quotes_gpc().

http://www.php-homepage.de/manual/function.addslashes.php




Zu deinem DB Script ist zu sagen,das es nur vom Server aus arbeitet. Dieses Script ist ja nur für deine Connection zur Db zuständig und gibt die Daten nicht an den Client zurück.

Sicher ist jedoch nichts..
;)
 
Allgemein sollten alle Parameter, auf die ein Außenstehender direkt oder indirekt Einfluss nehmen kann, validiert werden. Bei numerischen Parameterwerten wäre die Umwandlung der Parameterwerte in einen numerischen Typ angemessen (s. Typen-Umwandlung). Bei Zeichenketten wäre die Nutzung der mysql_real_escape_string()-Funktion eine gute Maßnahme sich vor gefährlichen Werten zu schützen.

Was die „magic_quotes“-Geschichte angeht, ist es möglich diese Einstellung entweder für die Laufzeit des Skriptes oder allgemein abzuschalten. Damit wäre auch die Frage nach der zusätzlichen Nutzung der stripslashes()-Funktion erledigt.

Zu deiner letzen Frage kann ich nur sagen, dass solange die Daten ordentlich als PHP-Konstanten oder -Variablen definiert sind, vor fremdem Zugriff geschützt sind. Auch sollte es nicht möglich sein, den Quellcode eines PHP-Skriptes über die Erweiterung phps einzusehen.
 
Ich würde folgende Syntax verwenden, falls Du mit dem Skript auch mal umziehst oder Dein Hoster die Eintellungen ändert:
PHP:
"... ".
    mysql_real_escape_string(
        ((get_magic_quotes_gpc()==1)
            ?stripslashes($name):$name)).
    " ..."
Damit entfernst Du die durch magic_quotes automatisch hinzugefügten Backslashes auch nur, wenn diese tatsächlich hinzugefügt wurden, theoretisch zumindest. Testen solltest Du das noch mal.
Wenn register_globals off ist, dann musst Du nur die Daten mit mysql_real_escape_string bearbeiten, die direkt oder indirekt (also Bestandteile einer Usereingabe enthalten) von einem Formular kommen. Dazu mußt Du bei im Skript definierten Variablen natürlich darauf achten, dass Du Dich nicht selbst "injectest".

Gegen Deinen letzten Absatz helfen u.a. folgende Maßnahmen:
Den Ordner mit der Konfiguration mit .htaccess schützen. So hat der User keinen direkt Zugriff auf die Datei.
"Sauber" programmieren und darauf achten, dass die definierten Variablen nur im gewünschten Kontext verwendet werden (z.B. nicht mit $$ oder eval arbeiten).
Du bist nun von der objektorientierung weg, jedoch lassen sich diese Daten in PHP5 auch in einer Datenbankklasse als private deklarieren und sind damit außerhalb der Klasse nicht sichtbar. Dann mußt Du "nur" noch darauf achten, dass die Daten nicht in irgendeinem Rückgabewert oder einer Ausgabe verwendet werden können.
Damit der .htaccess-Schutz nicht umgangen wird, musst Du darauf achten, dass nicht durch ein Skript ungeprüft Dateien eingebunden oder durchgeschleust werden, wie es z.B. häufig bei Bildergallerien mit den Bildern gemacht wird.

Gruß hpvw
 
Sonst könntest du dir auch eine Konstrukt zum Entfernen der Maskierungszeichen zusammenbastelt:
PHP:
<?php

	function stripslashes_deep($value) {
		return is_array($value)
			? array_map('stripslashes_deep', $value)
			: stripslashes($value);
	}

	if( get_magic_quotes_gpc()===1 ) {
		$_GET    = array_map('stripslashes_deep', $_GET);
		$_POST   = array_map('stripslashes_deep', $_POST);
		$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
	}

	…

?>
Noch etwas zu deiner letzen Frage: Du könntest auch mit folgendem Konstrukt prüfen, ob ein Skript nicht direkt aufgerufen wurde. Nur wenn es von einem weiteren Skript eingebunden wurde, bricht es nicht ab:
PHP:
<?php

	if( realpath($_SERVER['SCRIPT_FILENAME']) === realpath(__FILE__) ) {
		header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
		exit;
	}

	…

?>
 
Zuletzt bearbeitet:
Um nicht unnötige Informationen zu liefern würde ich 404 statt 403 senden, auch, wenn es gelogen ist.
 
Du hast recht, hqvw, der Status-Code 404 ist hier wirklich passender. Ich habe meinen vorherigen Beitrag entsprechend angepasst.
 
Erst einmal Dankeschön für die Beiträge. Daraus resultieren für mich nun folgende Verständnisfragen:

1) Bezugnehmend auf die Datei, die meine Datenbankdaten enthält schrieb Gumbo die Daten sollten "ordentlich" als PHP-Konstanten oder -Variablen definiert sein und es sollte nicht möglich sein, den Quellcode eines PHP-Skriptes über die Erweiterung phps einzusehen.

Hier mal der kurze Code:

PHP:
<?php
        //Paramter fuer die Datenbankverbindung
        $server = "localhost";
        $user = "root";
        $passwd = "";
        $datenbank = "votum";
        
        // Versuchen, die DB-Verbindung herzustellen
        $verbindung = mysql_connect ($server, $user, $passwd);
 if(!$verbindung) die("Datenbankverbindung konnte nicht hergestellt werden. Fehler " . mysql_errno() .": ". mysql_error());
        
        //Auswahl der entsprechenden Datenbank
 if (!mysql_select_db ($datenbank)) die("Datenbankverbindung konnte nicht hergestellt werden. Fehler " . mysql_errno() .": ". mysql_error());
        ?>
.
Diese Datei wird alse includet bzw. required in den Skripten, die mit der Datenbank arbeiten. DIe Querys sind dann in diesen Skripten.

Wie aber muss ich die Variablen nun in Gumbos Sinne "sicher" definieren? Und wie verhindere ich die Einsicht über phps (und was ist das überhaupt?


2) Numerische Werte vs. Zeichenketten

Bei numerischen Parametern empfiehlt Gumbo die Umwandlung in einen entsprechenden Variablentypen. Würde es dazu ausreichen nach der Eingabe durch den Nutzer numerische Variablen folgendermaßen in der Query zu spezifizieren?

PHP:
$result = mysql_query("select * from tabelle where besitzerid='(int) $besitzerid'");
      $result = mysql_query("insert into tabelle (besitzerid) values('(int) $besitzerid')");
Oder muss das an anderer Stelle mit settype() geschehen?

Resumierend sollte ich alse numerische Parameter wie hier beschrieben behandeln und Zeichenketten (z.B. Name oder Passwort) mit mysql_real_escape_string().

Ist das alles unter Punkt 2 soweit richtig?


3) magic_quotes_gpc()


Ich habe gelesen man könnte das auch für die Verwendung meiner Skripte deaktivieren? Wie geht das ? Auf die php.ini meines Servers habe ich leider keinen Zugriff und dort ist magic_quotes_gpc() aktiviert.

Aller weiteren Fragen (2 hätte ich sonst noch) hätten sich damit erledigt, wenn man die magic_quotes_gpc() für meine Skripte abschalten kann.

Vielleicht wärst Du so nett (Gumbo), DIch nochmals dazu zu äußern.

Tante Grazie
 
Zuletzt bearbeitet:
Vielleicht wärst Du so nett (Gumbo), DIch nochmals dazu zu äußern.
Gerne.

Zu deiner erstem Punkt: Solange das Skript wirklich nur als PHP-Quellcode vom Webserver behandelt wird, dürfte es nicht möglich sein den Inhalt der Variablen einzusehen. Du könntest die Daten zur Datenbankverbindung aber auch gleich direkt als Parameter der mysql_connect()-Funktion übergeben, ohne erst noch Variablen zu definieren. Desweiteren könntest du noch den zweiten Beispielcode aus meinem vorherigen Beitrag an den Anfang des Skriptes setzen um noch einmal mehr auf Nummer Sicher zu gehen.

Zu deinem zweiten Punkt: Hier einmal eine Beispiel-Abfrage:
PHP:
<?php

	$query = "
		INSERT INTO
		        `Tabelle`
		  SET
		        `Spalte-1` = ".intval($_POST['numerischer-Parameter']).",
		        `Spalte-2` = '".md5(trim($_POST['Passwort-oder-ähnlich-zu-behandelnder-Parameter']))."',
		        `Spalte-3` = '".mysql_real_escape_string($_POST['beliebige-Zeichenketten-Parameter'])."'
		";
	mysql_query($query) or die('<p><strong>Fehler bei der Datenbankabfrage:</strong> '.htmlspecialchars(mysql_error()).'</p><pre>'.htmlspecialchars($query).'</pre>');
	…

?>
Es ist auch meistens hilfreich direkt bei einer fehlgeschlagenen Datenbankabfrage die Abfrage selbst auszugeben – zumindest während der Entwicklung.

Zu deinem dritten Punkt: Mit der ini_set()-Funktion ist es möglich die Konfigurationseinstellungen für die Laufzeit des Skriptes zu ändern. Möchtest du eine Einstellung langfristiger ändern, ist dies möglicherweise mit der Apache-Direktive php_value möglich.
Sonst müsstest oder könntest du eben auf den ebenfalls in meinem vorherigen Beitrag beschrieben Algorithmus zum Entfernen der automatisch gesetzten Maskierungszeichen zurückgreifen. Damit sollte es dann auch funktionieren.
 
Danke, ich muss das morgen einmal alles an einem Beispiel durchprobieren um zu sehen, ob ich alles verstanden habe.

Ich poste das Ergebnis dann mal hierhin und stelle ggf. noch die ein oder andere Frage.
 
Zurück