Skript Sicherheit

schleckerbeck

Erfahrenes Mitglied
Hallo,

bin grad am basteln für mein CMS und schön langsam stellt sich die Frage zwecks Sicherheit.
Wie kann ich z.B. Variablen überprüfen, die per $_GET übertragen werden (es werden z.B. bestimmte Komponente per include($_GET[inc].'.php.') eingebunden), damit keine Sachen a lá index.php?inc=sehrboeserCode passieren können?

Desweiteren mach ich mir so einige Gedanken zwecks SQL Injections. Kennt jemand ne gute Lösung dies zu vermeiden? Habe eine Datenbank Klasse, über die sämtliche SQL Arbeiten laufen. Es sollte so funktionieren, dass der String mit dem Query "Select * from test where id='".$_GET['id'].'"" so überprüft wird, dass in der $_GET['id'] keine Sachen wie "; DROP DATABASE test;" drinnen stehen. Hab mich auch schon mit mysql_real_escape_string beschäftigt, steig da aber irgendwie nicht durch.

Hoffe ich hab mich einigermaßen verständlich ausgedrückt.
Vielleicht kennt jemand ja einen Ansatz.

Danke,
sc.
 
[phpf]mysql_real_escape_string[/phpf] ist eine gute Möglichkeit SQL Injektionen vorzubeugen. Eine andere wäre z. B. der Einsatz von PDO mit prepared Statements oder MySQLi.

Allerdings solltest du versuchen nicht nur die Daten zu filtern, sondern auch zu validieren.
Dabei können dir z. B. auch die Character Type Funktionen helfen.
Oder reguläre Ausdrücke.
 
Am besten schon garnix von aussen an die Db ran lassen. Wenn es sich nicht vermeiden läst dann eben sehr gründlich prüfen. Oft hilft da schon nur den richtigen datentyp zuweisen der erwartet wird.

Bsp bei Get angaben nur zahlen übergeben. Wenn nacher dann der Variable integer zuweißt kann kein böser code mehr kommen da nur zahlen angenohmen werden.

Bsp $var= int ($_GET['Wert']);
So wird wenn ein string übergeben wird daraus 0 und kann keinen schaden mehr anrichten.

Mfg Splasch
 
Naja, für die GET Werte werden aber auch alphanumerische Zeichen benötigt. Gibt's da nen regulären Ausdruck, damit nichts a lá "http://www.server.de/code.php" eingeschleust werden kann? Bin da leider ned so fit.

Zu den MySQL Statements:
PDO kann ich leider nicht verwenden, es muss unter PHP4 laufen. MySQLi unterstützen einige Provider nicht, wo die Skripte laufen sollen.
Die Character Type Funktionen glaub ich bringen mir auch nicht so viel, da bei einigen Skripten per GET zwangsläufig auch Daten übertragen werden, die evtl. auch mal Fließtext o.ä. enthalten.
Wie könnte man per reguläre Ausdrücke ein Statement filtern? z.B. wenn der Code eigentlich "Select * from test where id='$_GET[id]'" heißt, und ein potentieller Angreifer, wie oben schon mal erwähnt, in die $_GET[id] etwas wie "1'; DROP DATABASE test" einschleusen will?
Oder hat jemand noch ne elegantere Lösung? :)

btw: Welche sicherheitsrelevanten Aspekte würdet ihr bei so nem Projekt noch in Betracht ziehen?

Danke,
sc.
 
Da IDs ja im Allgemeinen > 0 und ganzzahlig sind kannst du dort schonmal folgendermaßen ansetzen:
PHP:
$id = intval(abs($_GET['id'])); // abs liefert den Betrag von $_GET['id']

Wenn es sich nur um ein Integer handelt, z.B. bei Timestamps oder sowas, dann kannst du, wie splasch schon erwähnt hat, [phpf]intval[/phpf] alleine benutzen. Strings werden mit [phpf]mysql_real_escape_string[/phpf] abgesichert, z.B:
PHP:
$boese = $_GET['sql'];
$machGut = mysql_real_escape_string($boese);

mysql_query("SELECT * FROM `blabla` WHERE `gut` = '{$machGut}'");

Bei den includes kann man mehrere Wege gehen, zum einen kannst du alle zulässigen Seiten einfach in ein Array eintragen:
PHP:
$validPages =array('home.php', 'about.php', 'usw.php');

if(!in_array($_GET['include'], $validPages))
{
    trigger_error('Hacking attempt...', E_USER_WARNING);
    exit;
}

include($_GET['include']);

Oder du prüfst, ob die Datei in deinem Dateisystem vorhanden ist:
PHP:
$include = $_GET['include'];
$currentDir = dirname(__FILE__);

if(!file_exists("{$currentDir}/{$include}") || !is_readable("{$currentDir}/{$include}"))
{
    trigger_error('Hacking attempt...', E_USER_WARNING);
    exit;
}

include($_GET['include']);

Das sind so zwei Wege, die man gehen kann...
 
Naja, für die GET Werte werden aber auch alphanumerische Zeichen benötigt

Für was bitte brauchst du bei Get werten alphanumerische Zeichen ? Ich denke das liegt doch rein daran wie man das ganze aufbaut und das hängt wiederum von Programmiere ab.

Und wie du oben schon die Id ansprichst für die ID sind eigentlich immer nur Zahlen vorgesehen schon allein wegen der Datenbankstruktur. Wer da jetzt anfängt mit Namen zu handieren der hat auch net wirklich viel Ahnung von einem Datenbank aufbau.

Zusätzlich zur sicherheit kanst du die standard php befehle nutzen.
htmlentities
http://at2.php.net/manual/de/function.htmlentities.php
addslashes
http://at2.php.net/manual/de/function.addslashes.php
mysql_real_escape_string
http://at2.php.net/manual/de/function.mysql-real-escape-string.php
unsw.

Mfg Splasch
 
Ich poste dir mal kurz, wie ich alle Mysql-Inserts und Updates behandle:

PHP:
<?php
class Mysql {
	public static function db_insert_data($table, $ins_hash)	{
		$cols = implode(",", array_keys($ins_hash));
		$data = implode(",", array_map(array("Mysql", "sql_check_values"), array_values($ins_hash)));

		$sql = "INSERT INTO ".$table." (".$cols.") VALUES (".$data.");";
		$query = @mysql_query($sql)
			or die(Mysql::report_error("Insert: ".mysql_error()."\nQuery: ".$sql));
		return(!$query ? false : true);
	}
	/***********************************************/
	public static function db_update_data($table, $upd_hash, $condition = "")	{
		$temp = array_map(array("Mysql", "prepare_update_hash"), array_keys($upd_hash), array_values($upd_hash));
		$settings = implode(",", array_values($temp));

		$condition = empty($condition) ? "" : " WHERE ".$condition;

		$sql = "UPDATE ".$table." SET ".$settings.$condition.";";

		$query = @mysql_query($sql)
			or die(Mysql::report_error("Update: ".mysql_error()."\nQuery: ".$sql));
		return(!$query ? false : true);
	}
	public static function sql_check_values($value) {
		if(get_magic_quotes_gpc()) $value = stripslashes($value);

		if(!is_numeric($value)) $value = "'". mysql_real_escape_string($value) ."'";

		return $value;
	}

	public static function prepare_update_hash($val1, $val2)	{ 
		return $val1."=".Mysql::sql_check_values($val2);
	}
}
?>

Eingetragen oder upgedatet wird dann so:
PHP:
<?php
$insert_update_hash['Field1'] = "Wert für Feld 1";
$insert_update_hash['Field2'] = "Wert für Feld 2";
[...]

Mysql::db_insert_data("table", $insert_update_hash);
//oder
Mysql::db_update_data("table", $insert_update_hash);
?>
Bei Bedarf können noch Rückgabewerte und so abgefangen werden.
Ich denke ich habe so weitgehend die Möglichkeiten für ungewünschte SQL-Statements eingedämmt. Vielleicht kannst du damit ja was anfangen.

Gruss Igäl
 
Ein weitere gute Möglichkeit ist es Datentyp formatierungen in Sql Statmens zu verwenden.

Ich mach das bsp mit sprintf

PHP:
$sql=sprintf("Insert into tabelle (Nname)values ('%s');",($Nname));

Wobei das %s zeichen dann für einen String wert steht so ist egal was in der Varible steht es wird immer nur als string behandel. Oder wenn man zahlen werte erwatet dann mit dem %d Zeichen für integer

Mehr darüber unter:
http://at2.php.net/manual/de/function.sprintf.php

Mfg Splasch
 
Danke für die vielen Antworten. Werd mich mal dransetzen, und das ganze durchprobieren!

Und wie du oben schon die Id ansprichst für die ID sind eigentlich immer nur Zahlen vorgesehen schon allein wegen der Datenbankstruktur. Wer da jetzt anfängt mit Namen zu handieren der hat auch net wirklich viel Ahnung von einem Datenbank aufbau.
Natürlich sind meine ID's Zahlen. Wollte eben eine "all-in-one" Lösung. :)

Danke,
sc.
 
@splasch: Wo genau steht geschrieben, dass ID's Zahlen sein müssen? Das schein ich verpasst zu haben. Ist aber auch gut möglich, dass ich nicht viel Ahnung von ERM-Aufbau habe, aber meiner Meinung nach, müssen ID's einfach beigezogen werden können um Datensätze zu identifizieren. Das können (müssen aber nicht ()) Zahlen sein. Aber was für Probleme siehst du bei ID's wie beispielsweise solcher Artikelnummern:
- #511.221
- #511.222

ID's könnten doch ohne Probleme auch "normale" Zeichenfolgen sein wie:
- mod_guestbook
- mod_users
- conf_user_access_table

Die einzige Bedingung die ID's zu erfüllen haben (wenn sie dann zur Identifikation von Datensätzen gebraucht werden [wofür sie mMn auch gedacht sind :) ]) ist, dass sie eindeutig sind.

In diesem Sinne: Prost
De Igäl
 
Zurück