SQL Injection vermeiden

WorldRacer

Erfahrenes Mitglied
Hallo zusammen,

ich bin gerade dabei, einen Webservice für mein C# Projekt zu entwickeln. Dabei werden Abfragen an den Webservice abgesetzt, die einen Filter enthalten. Dieser Filter ist im SQL-Format geschrieben, weshalb ich diesen nicht direkt in das SQL Template einsetzen möchte, um SQL-Injections zu vermeiden. Ich möchte gerne wissen, welche Möglichkeiten ich da habe. Reicht es mir schon aus, alleinstehende Keywords der Data Manipulation Language zu filtern? Also diese hier?

http://en.wikipedia.org/wiki/Data_manipulation_language hat gesagt.:
SELECT ... FROM ... WHERE ...
INSERT INTO ... VALUES ...
UPDATE ... SET ... WHERE ...
DELETE FROM ... WHERE ...

Wenn nicht, was kann ich dann machen? Ich meine, in den Microsoft WCF Services ist es ja auch möglich zu filtern. Ich möchte diese Funktionalität auch in einem PHP-Service nicht missen.

Eine Beispiel-URL, so wie ich sie aktuell bekomme, wäre:

http://example.com/api/storage/Customers/CustomerId = 1 AND CustomerName = 'xyz'

LG
Marco
 
Soweit ich weiß kann MySQL selber nichts bereinigen (stell ich mir auch eher schweirig vor), dass muss dann schon die jeweils gewählte Programmiersprache machen.
 
Hallo alxy,
darum habe ich ja ins PHP Forum gepostet. Es würde keinen Sinn machen, den MySQL-Server direkt ins netz zu hängen, darum auch der webservice.

Hier der Weg der Anfrage;

C# => HTTP => PHP => MySQL ===[Antwort]===> PHP => HTTP => C#
 
Ah, ok.

Dann verwende, wenn du die veraltete mysql-Erweterung benutzen willst(prozedural) mysql_real_escape_string() oder die PDO extension. Die ist komplett objektorientiert, bietet auch Unterstützung für von MySQL verschiedene DBMS und führt das escaping bei korrektem Einsatz(binding) automatisch durch: http://de3.php.net/manual/de/book.pdo.php
 
Du hast mich noch nicht ganz verstanden. Wenn ich den folgenden String escape, bringt er mir nichts mehr....

CustomerId = 1 AND CustomerName = 'xyz'

Ich kann ihn aber nicht direkt einbinden, wenn ich keine SQL Injection haben möchte. Ich möchte irgendeinen Weg finden wie ich diesen String verwenden kann (auch wenn ich diesen nur überprüfe, etc).
 
Man escaped ja auch nicht ein ganzes Statement, sondern die werte (in dem Fall 1 und xyz). Ich glaube, dass konept deines Webservices ist auch etwas falsch. Du musst dir eine Art Schnittstelle überlegen, immer die ganzen Queries zu übergeben ist ungeschickt und gefährlich.
Möglich wäre es, die jeweiligen Paramter über $_GET zu übergeben und dann diese dann zu escapen. Schau dir dazu beispielsweise mal die AWS an ;) Du kanst als Sicherheitsstufe auchnoch mit einer Signatur arbeiten und die Suchwerte kodieren/verschlüsseln. Mal so als Beispiel: service.php?do=lookup&id=1&name=xyz
 
Hallo alxy,

ich glaube nicht, dass das Konzept meines Webservices falsch ist, denn dann wäre auch das Konzept der Data-Services von Microsoft falsch. Dort werden die Queries so übergeben:

http://localhost:65363/NorthwindService.svc/Products?$filter=Discontinued eq true and UnitPrice ge 50

Und genau SO möchte ich das haben. Und mit der Bedingung, dass ich nicht angreifbar bin!
 
Ich versuch es mal mit einem Ansatz:

Es gibt den sog. Query-String, also das, was nach dem ? kommt. Das wäre in deinem Falle jetzt einfach mal "$filter=Discontinued eq true and UnitPrice ge 50". Soweit so gut. Das jagst du erst mal durch die Funktion urldecode():

PHP:
<?php
$params = urldecode($_SERVER['QUERY_STRING']);

var_dump($params);

Anschließend wirfst du erstmal dieses $filter= raus:

PHP:
<?php
$params = urldecode($_SERVER['QUERY_STRING']);
$params = str_replace('$filter=', '', $params);

var_dump($params);


Dann kannst du $params erstmal in seine Bestandteile aufsplitten. Als Trenner nimmst du " and " bzw. " or ":

PHP:
<?php
$params = urldecode($_SERVER['QUERY_STRING']);
$params = str_replace('$filter=', '', $params);
$params = preg_split('/and|or/', $params);

var_dump($params);

Jetzt vielleicht noch trimmen:

PHP:
<?php
$params = urldecode($_SERVER['QUERY_STRING']);
$params = str_replace('$filter=', '', $params);
$params = preg_split('/and|or/', $params);
$params = array_map('trim', $params);

var_dump($params);

Jetzt schreiben wir uns eine Funktion, die wir wieder durch array_map() schicken, und die nur einen Zweck hat, die Parameter in ein Key-Value-Paar zu parsen:

PHP:
function split_params($string)
{
	$search = array('/eq/', '/ge/', '/le/', '/gt/', '/lt/');
	$repl   = array('=', '>=', '<=', '>', '<');
	
	$key_value = preg_replace($search, $repl, $string);
	
	$result = preg_split('/(=|>=|<=|>|<)/', $key_value, NULL, PREG_SPLIT_DELIM_CAPTURE); 
	$result = array_map('trim', $result);
	return $result;
}

Anschließend kannst du das $params Array noch durch mysql_real_escape_string() schicken, um den Input zu entschärfen. Die Einzelteile kannst du dann anwenden wie du willst:

PHP:
<?php
function split_params($string)
{
	$search = array('/eq/', '/ge/', '/le/', '/gt/', '/lt/');
	$repl   = array('=', '>=', '<=', '>', '<');
	
	$key_value = preg_replace($search, $repl, $string);
	
	$result = preg_split('/(=|>=|<=|>|<)/', $key_value, NULL, PREG_SPLIT_DELIM_CAPTURE); 
	$result = array_map('trim', $result);
	return $result;
}

$params = urldecode($_SERVER['QUERY_STRING']);
$params = str_replace('$filter=', '', $params);
$params = preg_split('/and|or/', $params);
$params = array_map('trim', $params);

$params = array_map('split_params', $params);

var_dump($params);

Ergebnis:

Code:
array (size=2)
  0 => 
    array (size=3)
      0 => string 'Discontinued' (length=12)
      1 => string '=' (length=1)
      2 => string 'true' (length=4)
  1 => 
    array (size=3)
      0 => string 'UnitPrice' (length=9)
      1 => string '>=' (length=2)
      2 => string '50' (length=2)

Hilft das weiter?
 
So, hatte noch nicht die Zeit, aber werde mich heute Abend mal um die entsprechende Umsetzung kümmern.

//EDIT: Natürlich hier auch frohes Neues an alle Leser :)
 
Zurück