2010-08-01 8 views
4

J'essaie de défendre le formulaire de connexion sur ma page à partir d'injections SQL. Sur le côté serveur, j'utilise Zend Framework (Zend_Db, Zend_Db_Table_Abstract), mais ses fonctions intégrées de prévention d'injection: quote, quoteInto, quoteIdentifier ne font pas bien leur travail (dans la mesure où je sais comment les utiliser). D'autres moyens comme mysql_real_escape_string, addslashes ne semblent pas fonctionner du tout ...Prévention des injections SQL avec PHP et Zend Framework - comment?

C'est ce que je suis en train de mettre en œuvre pour la défense:

function prevent_from_sql_injection($str) { 
    if(preg_match('/[\'"]/', $str)) 
    {die('attack1'); exit; }// no quotes 
elseif(preg_match('/[\/\\\\]/', $str)) 
    {die('attack2'); exit; }// no slashes 
elseif(preg_match('/(and|or|null|not)/i', $str)) 
    {die('attack3'); exit; }// no sqli boolean keywords 
elseif(preg_match('/(union|select|from|where)/i', $str)) 
    {die('attack4'); exit; }// no sqli select keywords 
elseif(preg_match('/(group|order|having|limit)/i', $str)) 
    {die('attack5'); exit; }// no sqli select keywords 
elseif(preg_match('/(into|file|case|LOAD_FILE|DUMPFILE|char|schema|AES_DECRYPT|AES_ENCRYPT)/i', $str)) 
    {die('attack6'); exit; }// no sqli operators 
elseif(preg_match('/(--|#|\/\*)/', $str)) 
    {die('attack7'); exit; }// no sqli comments 
elseif(preg_match('/(=|&|\|)/', $str)) 
    {die('attack8'); exit; }// no boolean operators 
elseif(preg_match('/(UNI\*\*ON|1 OR 1=1|1 AND 1=1|1 EXEC XP_)/', $str)) 
    {die('attack9'); exit; } 
elseif(preg_match('/(1|'| |O|R|=|&#49&#39&#32&#79&#82&#32&#39&#49&#39&#61&#39&#49|%31%27%20%4F%52%20%27%31%27%3D%27%31)/', $str)) 
    { die('attack10'); exit; } 
elseif(preg_match('/(SELECT\s[\w\*\)\(\,\s]+\sFROM\s[\w]+)| (UPDATE\s[\w]+\sSET\s[\w\,\'\=]+)| (INSERT\sINTO\s[\d\w]+[\s\w\d\)\(\,]*\sVALUES\s\([\d\w\'\,\)]+)| (DELETE\sFROM\s[\d\w\'\=]+)/', $str)) 
    { die('attack11'); exit; } 
elseif(preg_match('/(script)|(<)|(>)|(%3c)|(%3e)|(SELECT) |(UPDATE) |(INSERT) |(DELETE)|(GRANT) |(REVOKE)|(UNION)|(<)|(>)/', $str)) 
    { die('attack12'); exit; } 
elseif(!preg_match('/^["a-zA-Z0-9\040]+$/', $str)) 
    { die('attack13'); exit; } 
else return $str; 

} 

pour tester mes résultats, j'utilise extension Firefox SQL Inject Me et il montre 14 autres erreurs (parfois 21 ou 17 et je ne sais pas pourquoi les résultats sont différents):

Server Status Code: 302 Found 
Tested value: 1' OR '1'='1 
Server Status Code: 302 Found 
Tested value: 1 UNI/**/ON SELECT ALL FROM WHERE 
Server Status Code: 302 Found 
Tested value: &#49&#39&#32&#79&#82&#32&#39&#49&#39&#61&#39&#49 
Server Status Code: 302 Found 
Tested value: 1 OR 1=1 
Server Status Code: 302 Found 
Tested value: 1' OR '1'='1 
Server Status Code: 302 Found 
Tested value: 1 EXEC XP_ 
Server Status Code: 302 Found 
Tested value: 1 UNION ALL SELECT 1,2,3,4,5,6,name FROM sysObjects WHERE xtype = 'U' -- 
Server Status Code: 302 Found 
Tested value: %31%27%20%4F%52%20%27%31%27%3D%27%31 
Server Status Code: 302 Found 
Tested value: 1 AND 1=1 
Server Status Code: 302 Found 
Tested value: 1' OR '1'='1 
Server Status Code: 302 Found 
Tested value: 1 AND ASCII(LOWER(SUBSTRING((SELECT TOP 1 name FROM sysobjects WHERE xtype='U'), 1, 1))) > 116 

Alors, quelle est la meilleure façon de prévenir toutes ces attaques par injection SQL? Utiliser des espaces réservés est bon mais cela ne marche pas dans certains cas. Peut-être que cette extension est fausse et j'ai une paranoïa?

+0

Définissez comment les autres options que vous avez mentionnées "ne fonctionnent pas bien". –

+4

Avez-vous considéré que certains d'entre eux ne sont que des rapports de flase? Tout simplement parce que le serveur renvoie le code 302, ne signifie pas que l'injection a réussi. – Mchl

+0

Peut-être que vous avez raison et que cet ext renvoie des résultats incorrects. Mais comment puis-je tester pour l'injection sql? – user406136

Répondre

7

Je recommande fortement l'utilisation de Zend_DB. Il utilise prepared statements.
Les paramètres des instructions préparées n'ont pas besoin d'être quotés; le conducteur gère automatiquement cela.

Si une application utilise exclusivement les instructions préparées , le développeur peut être sûr qu'aucune injection SQL se (cependant, si d'autres parties de la requête sont en cours de construction avec entrée déprotégés injection SQL est encore possible

$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'  => '127.0.0.1', 
    'username' => 'webuser', 
    'password' => 'xxxxxxxx', 
    'dbname' => 'test' 
)); 

$stmt = $db->query('SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?', 
    array('goofy', 'FIXED') 
); 

$rows = $stmt->fetchAll(); 

echo $rows[0]['bug_description']; 
+0

Ces params dans le tableau - sont-ils cités automatiquement ou quoi? – user406136

+1

Oui. C'est ce que les déclarations préparées sont pour. "Les paramètres des instructions préparées n'ont pas besoin d'être quotés, le pilote gère automatiquement cela." Veuillez lire la source liée pour plus d'informations. –

+0

Je suis d'accord avec Benjamin, l'utilisation de Zend_Db supprime tout le souci des instructions préparées et je n'ai eu aucun problème avec cela. –

7

Utilisez des instructions SQL préparées au lieu de l'échappement des valeurs. La mise en liste noire de quelques instructions SQL n'est pas une approche judicieuse. Cette extension Firefox est trompeuse et vous donne une mauvaise impression.

Évidemment, vous ne devriez pas commencer à accepter des commandes SQL aléatoires en entrée. Au mieux, vous utilisez certaines variables de requête comme données de requête. Et les déclarations préparées sont suffisantes pour assurer la sécurité dans ce cas. La concaténation des commandes SQL et des variables est une pratique obsolète (ce que cette extension Firefox discutable semble suggérer).

+0

Et c'est exactement ce que propose Zend_Db. – Mchl

+0

Que faire avec Zend_Auth_Adapter_DbTable? – user406136

+1

effectivement il n'y a pas de problème avec échapper si utilisé correctement –

-1

Mettez ce code dans votre page d'en-tête.

function clean_header($string) 
{ 
$string = trim($string); 

// From RFC 822: “The field-body may be composed of any ASCII 
// characters, except CR or LF.” 
if (strpos($string, “\n“) !== false) { 
$string = substr($string, 0, strpos($string, “\n“)); 
} 
if (strpos($string, “\r“) !== false) { 
$string = substr($string, 0, strpos($string, “\r“)); 
} 

return $string; 
}