You are not logged in.

  • Login

1

Thursday, January 22nd 2009, 9:47pm

Wichtige Sicherheitsfrage: SQL Injection

Meine Frage:
Wenn folgender Code am Anfang der Datei includiert wird,
müssen später die globalen Variablen nicht mehr ecaped werden?
Code:

PHP Quellcode

1
2
3
4
5
6
7
8
if(get_magic_quotes_gpc() == 0) {
  foreach($_GET as $key => $value)      { $_GET[$key]      = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
  foreach($_POST as $key => $value)     { $_POST[$key]     = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
  foreach($_SERVER as $key => $value)   { $_SERVER[$key]   = htmlspecialchars(addslashes($value),       ENT_QUOTES, 'UTF-8'); }
  foreach($_COOKIE as $key => $value)   { $_COOKIE[$key]   = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
  foreach($_SESSION as $key => $value)  { $_SESSION[$key]  = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
}
set_magic_quotes_runtime(0);

Kann man dann solche SQL-Anfragen verwenden?

SQL Code

1
SELECT memberid FROM members WHERE membername = $_POST['name']

2

Thursday, January 22nd 2009, 10:01pm

Hi.
Generell würde ich immer die Funktionen des Datenbankwrappers vorziehen. Sei es PDO, MySQLi oder die alte mysql_*** Schnittstelle.
Was du nicht abdeckst sind Mehrdimensionale Arrays. Wie du sie zum Beispiel bei Multiselects oder Checkboxen hast. Außerdem müssen Zahlen ohne Hochkommas getrennt behandelt werden.

Vollständiger Hack:

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$_POST['size'] = "1,text=(SELECT password FROM members WHERE membername=admin) ";
 
foreach($_GET as $key => $value)      { $_GET[$key]      = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
foreach($_POST as $key => $value)     { $_POST[$key]     = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
foreach($_SERVER as $key => $value)   { $_SERVER[$key]   = htmlspecialchars(addslashes($value),       ENT_QUOTES, 'UTF-8'); }
foreach($_COOKIE as $key => $value)   { $_COOKIE[$key]   = htmlspecialchars(trim(addslashes($value)), ENT_QUOTES, 'UTF-8'); }
 
echo "UPDATE members SET size = {$_POST[size]} WHERE memberid=5";
 
//output: UPDATE members SET size = 1,text=(SELECT password FROM members WHERE memberid=1) WHERE memberid=5
 
?>

3

Thursday, January 22nd 2009, 10:07pm

Was du nicht abdeckst sind Mehrdimensionale Arrays. Wie du sie zum Beispiel bei Multiselects oder Checkboxen hast. Außerdem müssen Zahlen ohne Hochkommas getrennt behandelt werden.

OK, aber momentan sind keine davon in Gebrauch und wenn doch dann werde ich den Code entsprechend ändern! :)
Wenn ich per str_replace noch ein paar "Badwords" filtere wie z.B. SELECT, FROM, WHERE usw. (die mit null ersetze) wäre es dann sicher?

PS: Danke schonmal für die schnelle Hilfe, scheint ein tolles Board zu sein! :D

E:

PHP Quellcode

1
set_magic_quotes_runtime(0);

Sollte entfernt werden oder? Weil das würde das escapen deaktiveren.
Also entweder entfernen oder vor die foreach-Schleifen setzen?

4

Thursday, January 22nd 2009, 10:16pm

Generell ist ein blacklistverfahren nie sicher.

Was ich dir empfehlen kann ist der HTML Purifier:
http://htmlpurifier.org/

Ich bin ja so ein kleiner Buch fetischist und kann dir auf jedenfall das Buch empfehlen.
Da bekommt man einen sehr guten Einblick über XSS, SQL Injection, CSRF ... etc. ...

Thursday, December 24th 2009, 12:29pm

By Christopher Kunz, Stefan Esser

PHP-Sicherheit: PHP/MySQL-Webanwendungen sicher programmieren

Absolute Sicherheit gibt es nicht. Aber bekannte Fehler lassen sich vermeiden. Diese Grundregel gilt auch für PHP -- Paranoia hilft nicht weiter, aber Nachlässigkeit ist Selbstverschulden -- Christopher Kunz und Stefan Esser schließen nun mit der 3., überarbeiteten Auflage ihres PHP-Sicherheit: PHP/MySQL-Webanwendungen sicher programmieren für viele engagierte PHP-Programmierer eine Wissenslücke auf dem neusten Stand und zeigen praktisch und leicht umsetzbar, wie sich PHP-basierte Webanwendungen gezi...



Leider sind nur meines erachtens 200Seiten des Buches brauchbar, aber die sind ihr Geld wert

5

Thursday, January 22nd 2009, 10:19pm

naja, das Problem sind nicht die Badwords sondern die leichtsinnige Verwendung von Zahlenfeldern.
Hier sollte man typsicherheit garantieren.

Anderes Beispiel:

PHP Quellcode

1
2
3
$_POST['memberid'] = "500 OR memberid > 0";
echo "UPDATE members SET password = md5('meinpasswort') WHERE memberid=".$_POST['memberid'];
//output: UPDATE members SET password = md5('meinpasswort') WHERE memberid=500 OR memberid > 0


Das Beispiel ist zugegeben unrealistisch... Aber jeder macht mal Fehler.
Die richtige Lösung wäre hier:

PHP Quellcode

1
echo "UPDATE members SET password = md5('meinpasswort') WHERE memberid=".intval($_POST['memberid']);


Aber wie gesagt: Wenn du einen "richtigen" Datenbankwrapper verwendest, ersparst du dir viel Mühe und Ärger.
Hier unsere Einführung in PDO

Anderes Beispiel:

PHP Quellcode

1
2
3
$_POST['size'] = "1,admin=1 ";
echo "UPDATE members SET size = {$_POST[size]} WHERE memberid=5";
//output: UPDATE members SET size = 1,admin=1 WHERE memberid=5

6

Thursday, January 22nd 2009, 10:25pm

Das mit intval ist mir bekannt :D
aber das sollte eher nur ein Beispiel sein,
es kommen eher Strings/Texte darin vor.
Und wie sieht es aus wenn ich jeden Query in meiner Datenbank-Klasse
vorher mit mysql_real_escape_string escape?

E:
Funktion zum Verarbeiten aller Arraywerte:

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
function addslashes_deep($value)
{
    $value = is_array($value) ?
                array_map('addslashes_deep', $value) :
                addslashes($value);
 
    return $value;
}
 
Also z.B.:
foreach($_POST as $key => $value)     { $_POST[$key]     = htmlspecialchars(trim(addslashes_deep($value)), ENT_QUOTES, 'UTF-8'); }

This post has been edited 1 times, last edit by "PHP-Fortg" (Jan 22nd 2009, 10:31pm)


7

Thursday, January 22nd 2009, 11:19pm

Ich will, genau wie Vince, auf entsprechende PHP-Literatur verweisen: PHP Literatur (inbs. die neuste Buchempfehlung zum Thema Sicherheit von mir). Sicherheitslücken sind derartig vielfältig, so ganz kann man sich wohl gar nicht schützen :).

8

Wednesday, February 11th 2009, 9:00pm

OK nochmal zusammenfassend:
Sicherheitslücken sind derart vielfältig das kein 100% Schutz besteht aber man kann doch vorbeugen:
Multidimensionale Arrays auch beachten
PDO verwenden
Wenn Zahlen erwartet: intval()

Kann man jetzt schon viel beruhigter sein in Bezug auf die Sicherheit?
Was gibt es noch zu beachten?

9

Thursday, February 12th 2009, 8:26am

Deine Konstruktion ist zwar sehr schön, aber pass auf, dass du dir nicht jegleiche Strings mit den slashes "zerstörst".

Hast du das schonmal ausprobiert? Alles was du da quotest übernimmt MySQL afaik mit in die Datenbank.

Nehmen wir an du hast ein String:

PHP Quellcode

1
$str = "Peter's";

Jetzt Quotest du:

PHP Quellcode

1
echo $str; // Ausgabe Peter\'s

Und so stehts dann auch in der Datenbank.

Wenn du pech hast und das ganze summiert sich, dann sieht dein String bei mehrfachen einlesen, speichern, auslesen so aus:
Peter\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'s
Und wenn du dann noch ein "TEXT"-Feld hast, dann summiert sich das ganze natürlich gegen unendlich und du Müllst die gesamte Datenbank voll.
Aus der oberen Ausgabe wird dann :
Peter\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'s
und beim nächsten mal:
Peter\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'s
usw. ...

Ich habe das selber mal bei einem selbst geschriebenen CMS mit einem WYSIWYG-Editor miterlebt, der dann logischerweise nicht die slashes angezeigt hatte.
Nach circa einem Jahr fand es die Datenbank überhaupt nicht mehr Lustig, dass sich tausende Texte immer wieder und wieder multipliziert hatten :D



Dann solltest du wie geschrieben PDO oder mysqli_ nutzen.
Dort gibt es prepared Statements.

http://de3.php.net/manual/de/mysqli.prepare.php

PHP Quellcode

1
2
3
4
5
6
7
8
9
10
11
$city = "Amersfoort";
 
/* create a prepared statement */
if ($stmt = mysqli_prepare($link, "SELECT District FROM City WHERE Name=?")) {
 
    /* bind parameters for markers */
    mysqli_stmt_bind_param($stmt, "s", $city);
 
    /* execute query */
    mysqli_stmt_execute($stmt);
....


Damit trennst du das reine SQL Statement und die Paramter. Beim binden übergibst du dann die Parameter und es wird dann automatisch von der Datenbank gequotet.

Und wie du hier sehen kannst:
http://de3.php.net/manual/de/mysqli-stmt.bind-param.php

gibt es beim binden auch die Möglichkeit gleich einen Datentyp anzugeben.

Quoted

types: A string that contains one or more characters which specify the types for the corresponding bind variables:


Damit sparst du dir dann auch das intval oder anders typecasting.


Zum AUsgangspost zurück zu kommen, ist es so wie du selber sagst, dass man niemals 100%ig sicher ist, aber beruhigter kannst du auf jedenfall sein.
Zu beachten gibt es ne Menge, vielleicht nicht mehr so viel in Bezug auf SQL Injection, aber es gibt ganz viele andere "tolle" Verfahren die man noch nutzen kann.


http://de.wikipedia.org/wiki/Cross-Site_Scripting
(http://www.wbb-security.de/support/progr…ermanipulation/)
http://de.wikipedia.org/wiki/Cross-Site_Request_Forgery

Und unterschätz das nicht, es gibt genug Kiddy "Schritt-für-Schritt"-Anleitungen und Tools wo das jeder machen kann.


Wie mehrmals geschrieben, wenn du das Geld entbehren kannst dann hol dir das oder ein vergleichbares Buch :)

Social bookmarks