SQL Injektion abwehren

G.P.

Legendäres Mitglied
Wie man leicht den Titel entnehmen kann geht es um die Abwehr von SQL Injektion.

Ich weiss leider nicht wie man am besten Variablen aus Texteingabefeldern am besten validiert, da sie ja eigentlich jeden Inhalt haben. Momentan markiere ich eigentlich nur die Sonderzeichen mit folgenden Befehl, aber das reicht sicher noch nicht aus.
Hat jemand eine guten Hinweis?


htmlentities($_POST['message'],ENT_QUOTES);

MfG
GP
 
erstelle eine Datei security.php darin fügst Du folgenden Code ein:
CODE <?
function RemoveXSS($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as <java\0script>
// note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
$val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val);

// straight replacements, the user should never need these since they're normal characters
// this prevents like <IMG SRC=&#X40&#X61&#X76&#X61&#X73&#X63&#X72&#X69&#X70&#X74&#X3A&#X61&#X6C&#X65&#X72&#X74&#X28&#X27&#X58&#X53&#X53&#X27&#X29>
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
//;? matches the;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars

// &#x0040 @ search for the hex values
$val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a;
// &#00064 @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a;
}

// now the only remaining whitespace attacks are \t, \n, and \r
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);

$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?';
$pattern .= '|(&#0{0,8}([9][10][13]);?)?';
$pattern .= ')?';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
return $val;
}



?>



Diese security.php muss nun auf JEDER Seite includet werden.
include"security.php";
 
Danke fuer die Datei.

Aber ein reines Includen reicht da nicht aus, oder?
Muss ich die zu ueberpruefenden Werte jetzt an die function uebergeben?

MfG
GP
 
Doch, das includen als erste Zeile Code sollte ausreichen.

Ich arbeite nicht mit funktionen, desshalb kann ich es nicht garantieren. Aber mach einfach schnell einen Test.
 
Ich ahbe es mal ausgestestet und die Funktion muss mit der zu ueberpruefenden Variablen aufgerufen werden.

Auch ist mir aufgefallen, das unter verwendung von "htmlentities" das ganze Script unnoetig wird.
Wenn ich das richtig sehe, schuetzt das Script nur vor "Fehlerhaften" HTML und Javascript Anweisungen. Unter der Verwendung von "htmlentities" sind diese aber gar nicht moeglich, da alle Tags maskiert werden.

MfG
GP
 
Meiner Meinung nach würde ich mich gegen MySql-Injections nicht auf RemoveXSS, das wohl eher auf Cross Site Scripting abzielt, verlassen.

Beispiel:
require_once('db.inc.php');
$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = (mysql_real_escape_string(RemoveXSS($order)));
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY $order");


Aufruf mit o=IF ( (SELECT userid FROM sql_injection_test WHERE username=0x57696e6e6965 AND password=0x31323334), userid, username)

geht ohne Probleme auch zusammen mit mysql_real_escape_string durch!
Das Statement sieht dann so aus: „SELECT userid, username FROM sql_injection_test ORDER BY IF ( (SELECT userid FROM sql_injection_test WHERE username=0x57696e6e6965 AND password=0x506f6f68), userid, username)”


Es gibt dazu einen super Artikel und ein „Testkit“ unter: http://webappsec.org/projects/articles/091007.shtml
 
Sql-Injektionen beruhen darauf, daß der Sql-Code on the fly zusammengebaut und daß durch Benutzereingaben zusätzliche Logik, also Elemente außerhalb von Parameterwerten, eingefügt werden.

Das Problem läßt sich vollständig vermeiden, wenn man auf das Zusammenbauen on the fly verzichtet und alle Zugriffe über gespeicherte Prozeduren abwickelt, denen Parameter übergeben werden. Damit wird der Code, die Logik, kompiliert und die Parameterwerte erst anschließend ergänzt. Die Logik läßt sich nun nicht mehr verändern.

Alles andere ist angesichts von Beispielen ähnlich zu denen, die @hatschi1810 gepostet hat, nur ein Kampf gegen Windmühlenflügel.
 
Wie Jürgen schreibt sind prepared statements sicher die beste Lösung, leider hinken da die meisten PHP-Anwendungen doch erheblich nach.
Mit meinem Beispiel wollte ich auch nur zeigen das sowohl mysql_real_escape_string als auch selbstgebastelte/kopierte Lösungen leider nicht 100% sicher sind.
 
Die Konsequenz heißt für mich einfach, daß entsprechende Programmierumgebungen (mySql-Version, die keine Stored Procedures unterstützt oder bewußte Nichtnutzung des entsprechendes Features der Datenbank durch die Entwickler) für ernstzunehmende Online-Anwendungen nicht geeignet bzw. nicht zulässig sind / sein dürfen.

Von PHP habe ich zu wenig Ahnung. Aber gibt es denn in PHP nicht eine Möglichkeit, sP aufzurufen und Parameter zu übergeben? Die Erstellung der sP macht zwar ziemlich viel Arbeit, aber damit ist das Problem der Sql-Injektionen lösbar.

Voraussetzung ist natürlich ein passendes Backend - Postgres oder eine der neueren mySql-Versionen.
 
mysql_real_escape_string bei der eingabe und htmlentities bei der ausgabe reichen vollkommen aus.
 
Zurück
Oben