web-dev-qa-db-fra.com

Comment la procédure stockée empêche-t-elle l'injection SQL?

Je ne suis peut-être pas très clair sur la procédure stockée. Quelqu'un peut-il m'expliquer comment la procédure stockée empêche l'injection SQL avec un exemple simple utilisant MySql.

14
Anandu M Das

Les procédures stockées sont une forme de requête paramétrée. Le problème fondamental qui provoque l'injection SQL est que les données sont traitées comme un langage de requête.

$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

Dans cet exemple, si je définit $password à foo' OR 'x'='x, nous obtenons ceci:

SELECT * FROM users WHERE username = 'blah' AND password = 'foo' OR 'x'='x'

Étant donné que le caractère "x" est toujours égal au caractère "x", cette requête renvoie toujours des lignes, que l'utilisateur/passe soit correct. La base de données ne peut pas savoir que vous ne vouliez pas cela, car elle ne reçoit qu'une chaîne sans contexte.

Pour éviter cela, vous devez être capable de connaître la différence entre la requête et les données. Les procédures stockées résolvent ce problème en écrivant la requête au préalable, avec des marqueurs pour les paramètres, afin que les données puissent leur être transmises ultérieurement. Par exemple:

SELECT * FROM users WHERE username = ? AND password = ?

Le pilote de base de données envoie le nom de cette procédure stockée (ou, dans les requêtes paramétrées standard, uniquement le texte de la requête lui-même) et une liste de paramètres, en tant qu'entités distinctes distinctes dans le protocole. Cela signifie que le serveur de base de données peut analyser la chaîne de requête en tant que langage de requête en toute sécurité et traiter les paramètres uniquement comme des données, sans aucune ambiguïté.

J'ai aussi écrit une réponse plus longue il y a un certain temps qui explique tout cela de manière plus verbeuse, si cela vous est utile.

14
Polynomial

Les procédures stockées ne sont pas à l'abri de l'injection SQL . Comme expliqué ici:

https://blogs.msdn.Microsoft.com/brian_swan/2011/02/16/do-stored-procedures-protect-against-sql-injection/

Tant que du SQL dynamique peut être créé à l'intérieur des procédures stockées, vous êtes vulnérable à l'injection SQL.

Et MySQL à partir de 5.0.13, ont une capacité SQL dynamique:

https://stackoverflow.com/questions/190776/how-to-have-dynamic-sql-in-mysql-stored-procedure

Et est donc vulnérable à l'injection SQL.

Dans SQL Server , voici un exemple:

http://www.sqlinjection.net/advanced/stored-procedure/

VULNERABLE STORED PROCEDURE USING EXEC STATEMENT.
CREATE PROCEDURE getDescription
   @vname VARCHAR(50)
AS
   EXEC('SELECT description FROM products WHERE name = '''+@vname+ '''')
RETURN

Et un autre exemple:

VULNERABLE STORED PROCEDURE USING DYNAMIC CURSOR.
CREATE PROCEDURE printDescriptions
   @vname             VARCHAR(100)
AS
   DECLARE @vdesc VARCHAR(1000)
   DECLARE @vsql VARCHAR(4000)
   SET @vsql = 'SELECT description FROM products WHERE name='''+@vname+''''
   DECLARE cur CURSOR FOR EXEC @vsql

   OPEN cur
   FETCH NEXT FROM cur INTO @vdesc

   WHILE @@FETCH_STATUS = 0
   BEGIN
      PRINT @vdesc
      FETCH NEXT FROM cur INTO @vdesc
   END

   CLOSE cur
   DEALLOCATE cur
RETURN

Et dans Oracle , voici les exemples:

http://software-security.sans.org/developer-how-to/fix-sql-injection-in-Oracle-database-code

https://www.blackhat.com/presentations/bh-europe-04/bh-eu-04-litchfield.pdf

La présentation ci-dessus par David Litchfield dans Blackhat Europe a quelque chose de plus grave, de l'injection SQL, cela peut conduire à une élévation de privilèges - donc un utilisateur Oracle normal peut fonctionner en tant que DBA si les procédures stockées sont créées par DBA (par exemple, tous les objets système Oracle).

4
Peter Teoh

Une base de données SQL fonctionne une instruction en plusieurs étapes. Au début, le test de l'instruction SQL est analysé, puis il sera optimisé et compilé. Lorsque cela est terminé, la base de données dispose désormais d'un logiciel interne qui peut exécuter l'instruction SQL donnée.

Les procédures stockées sont précompilées. En d'autres termes, la base de données crée ce logiciel interne avant de l'utiliser. Dans ce cas, seul le code de programme est interprété sans aucune influence des paramètres.

Si vous passez un paramètre inclus d'instruction SQL complète à la base de données, il traite les étapes décrites ci-dessus.

Par exemple ...

SELECT * FROM myTable WHERE id=1

ou vous donnez quelque chose comme ça ...

SELECT * FROM myTable WHERE id=1;DROP TABLE myTable

Normalement, personne n'écrirait une instruction comme la seconde dans son code de programme, mais si vous prenez par exemple des paramètres directs d'une requête Web, il est possible qu'une telle instruction en résulte.

var sqlString="SELECT * FROM myTable WHERE id=";
sqlString = sqlString+request.getParameter("id");
// database parse, compile and optimize
var result=database.doQuery(sqlString);

Si vous utilisez des procédures stockées ou une déclaration préparée. Le processus d'analyse et de compilation est déjà effectué dans la base de données. Toute l'interprétation dépend de votre code de programme. Lorsque vous l'appelez, la base de données n'insère que les paramètres donnés dans le code précompilé et il en trouve les types de données acceptés.

var sqlString = "call queryMyTable(?)";
// get the precompiled statement from database
var statement = database.createStatement(sqlString);
// inject the parameter
statement.setParameter(1,request.getParameter("id"));
// if 'id' is a number it works fine ...
// but if 'id' is '1;DROP TABLE myTable' you will got a type cast error and the risk of SQL injection is banned
var result = statement.execute();

Les procédures stockées et les déclarations préparées sont égales en termes de sécurité.

3
OkieOth

Une autre façon d'y penser, d'être explicite et d'augmenter les réponses déjà données.

SELECT * FROM users WHERE username = 'blah' AND password = 'foo' OR 'x'='x'

Sans l'instruction préparée, le OR après le 'foo' est traité comme code

Maintenant, avec l'instruction préparée du point de vue de la base de données, le mot de passe essayé est:

'foo' OR 'x'='x'

I.E le OR (et tout le reste) après que 'foo' soit traité en tant que données

0
james6125