SQL Injection …

Titre aguicheur, mais vos bases de données sont elles protégées face à une attaque de type injection SQL ?

Un certain nombre de mythes existent sur SQL Server. Dont un qui laisserait entendre que si l’on accède systématiquement aux données au travers de procédure stockées, on se prémunit contre des attaques de ce type.

Voici une petite démonstration tendant à prouver el contraire :

Tout d’abord, nous allons créer une base et une table tout simple.

USE MASTER;
GO

CREATE DATABASE Test_SQLInjection;
GO

USE Test_SQLInjection;
Go

CREATE TABLE Certifications_Microsoft (
    idCertification INT PRIMARY KEY NOT NULL IDENTITY (1,1),
    Libelle VARCHAR(100)
                                        );
GO

INSERT INTO Certifications_Microsoft (Libelle)
VALUES     (‘MCT’),
        (‘MVP SQL Server’),
        (‘MCITP Database Administrator’),
        (‘MCITP Database Developer’),
        (‘MCITP Enterprise Administrator’),
        (‘MCITP Server Administrator’),
        (‘MCDBA (7.0, 2000)’),
        (‘MCSE (NT4 , 2000, 2003), MCSA’),
        (‘MCSD, MCAD’);
GO

Un rapide select sur la table nous montre que la table est correctement renseignée.

image

Nous allons maintenant créer une procédure stockée toute simple afin d’effectuer une recherche dans cette table.

CREATE PROCEDURE usp_GetCertification
(
    @certification VARCHAR(100)
)
AS
    SET NOCOUNT ON

    DECLARE @sql   VARCHAR(MAX)
    DECLARE @where VARCHAR(MAX) =  »

    IF @certification IS NOT NULL BEGIN
        SET @where = ‘ AND Libelle LIKE  »’
            + @certification + ‘% »’
    END

    SET @sql = ‘SELECT * FROM dbo.Certifications_Microsoft WHERE 1=1 ‘ + @where
    PRINT @sql
    EXEC(@sql)
GO

L’instruction PRINT est là seulement à des fins de débug, pour afficher l’instruction SQL que le moteur va exécuter comme nous allons le voir par la suite.

Exécutons la procédure :

image

Pas de soucis, les informations renvoyées sont bien correctes.

Imaginons maintenant que d’une manière ou d’une autre, une personne mal intentionnée arrive à connaitre le nom de la table qui se cache derrière votre procédure stockée. Ou bien, dans des cas bien plus réels, que votre base comporte des tables aux noms plutôt classiques tels que Order(s), User(s), Product(s), etc … En français, en anglais …

Essayons donc une petite injection de code T-SQL:

usp_GetCertification ‘MCITP »;DELETE Certifications_Microsoft –‘;

L‘instruction PRINT nous permet alors de voir (et de comprendre) l’ordre SQL qui a été traité par SQL Server :

image

l’instruction SELECT est suivie d’un DELETE …

L’instruction DELETE va être exécutée par le moteur … Syntaxiquement, la requête est correcte, pas de problème de résolution de nom, pas de problème de droit. Le moteur n’a pas de raison de ne pas exécuter cette requête.

Le fait d’avoir mis du code dynamique dans la procédure stockée nous a exposé à ce type d’attaque.

Quels enseignement tirer de cette démonstration :

  1. Avoir des règles de nommage des objets qui ne permettent pas forcément de “trouver” un nom de table en se basant sur un dictionnaire ….
  2. Ne pas donner trop de droits aux connexions SQL. Notre exemple fait un simple DELETE, mais si la personne avait été DB_OWNER, un drop table aurait été possible …. Voire bien pire si le login est SYSADMIN. On ne rigole pas, on pourrait faire un sondage sur le nombre de chaines de connexion utilisant ‘sa’ comme login … Data_reader, data_writer et le droit EXECUTE sur le schema afin de permettre l’exécution des procédure stockées.
  3. Tester systématiquement els paramètres, dans la procédure stockée, mais aussi dans le code client.
  4. Ne jamais afficher de requête SQL sans une URL de page Web !!!!

Finalement, dans ce cas précis le problème vient du code dynamique. En fait, pas tout a fait, cela vient surtout de l’instruction EXEC. Lorsque je dispense des formations développement SQL Server, ou lors de mes interventions en tant que consultant SQL Server, je conseille toujours très vivement de limiter le code dynamique dans les procédures stockées.

Néanmoins, si l’on veut utiliser du code dynamique, je recommande très vivement de ne JAMAIS utiliser l’ordre EXEC, mais de systématiquement passer par l’instruction SP_EXECUTESQL qui, elle, nous permet de se protéger contre des attaques de type injection SQL.

Démonstration :

Nous allons commencer par ajouter des données, vu qu’on les a perdu !!!!

INSERT INTO Certifications_Microsoft (Libelle)
VALUES     (‘MCT’),
        (‘MVP SQL Server’),
        (‘MCITP Database Administrator’),
        (‘MCITP Database Developer’),
        (‘MCITP Enterprise Administrator’),
        (‘MCITP Server Administrator’),
        (‘MCDBA (7.0, 2000)’),
        (‘MCSE (NT4 , 2000, 2003), MCSA’),
        (‘MCSD, MCAD’);
GO

Nous allons maintenant ré-écrire la procédure stockée en utilisant un SP_EXECUTESQL :

CREATE PROCEDURE usp_GetCertification_safe
(
    @certification VARCHAR(100)
)
AS
    SET NOCOUNT ON

    DECLARE @sql   NVARCHAR(MAX)
    DECLARE @params NVARCHAR(MAX) = ‘@certif VARCHAR(100)’

    SET @sql = ‘SELECT * FROM dbo.Certifications_Microsoft WHERE 1=1 ‘
    IF @certification IS NOT NULL BEGIN
        SET @sql = @sql + ‘ AND Libelle LIKE (@certif +  »% » )’
    END

    EXEC sp_executesql @sql, @params, @certif = @certification
GO

L’appel de la procédure avec un paramètre “normal” fonctionne bien et l’appel avec une tentative d’injection SQL échoue. La procédure ne produit aucun résultat mais ne vide pas la table …

image

Le dernier enseignement à retenir, c’est qu’il vaut mieux éviter d’instruction EXEC …

Je vous recommande donc de migrer le code de vos procédures pour utiliser SP_EXECUTESQL.

A propos Christophe

Consultant SQL Server Formateur certifié Microsoft MVP SQL Server MCM SQL Server 2008
Cet article a été publié dans SQL Server. Ajoutez ce permalien à vos favoris.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s