lundi 27 novembre 2017

[DEI] SQL Injection exemple

"La faille SQLi, abréviation de SQL Injection, soit injection SQL en français, est un groupe de méthodes d'exploitation de faille de sécurité d'une application interagissant avec une base de données. Elle permet d'injecter dans la requête SQL en cours un morceau de requête non prévu par le système et pouvant en compromettre la sécurité." (Wikipedia)

Pour démontrer cette attaque, il nous faut un système qui interagit avec une base de données. Ainsi, nous prennons la base de données simple suivants :

Create Database SQLInjection;

Create Table User (
    id char(100) Primary Key,
    password char(100) Not Null
);

Insert into User Values ('admin', 'admin');

Le code ci-dessus devrait fonctionner sur tous les SGBD-R (ou au moins la plus part). Nous créons, ainsi, une seule table qui sert à authenrifier les utilisateurs. L'objectif de l'attaque sera, dans ce cad, de réussir l'authentification sans avoir un id et un password valides.

L'application victime se compose de deux classes. La première classe garantir l'accès à la base de données. La méthode qui nous intéresse est la méthode "verifierUser()" qui retroune Vrai si l'authentification a réussi, c'est à dire, s'il existe au moins un utilisateur avec le id et le password saisis (le SGBD utilisé est MySQL) :

package bdd;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 *
 * @author Tarek Boutefara <t_boutefara@esi.dz>
 */
public class AccesBDD {
    
    public static Connection connexion;
    
    public static Connection getConnexion() throws ClassNotFoundException, SQLException{
        if(connexion == null){
            Class.forName("com.mysql.jdbc.Driver");
            connexion = DriverManager.getConnection("jdbc:mysql://localhost/SQLInjection", "tarek", "");
        }
        return connexion;
    }
    
    public static boolean verifierUser(String user, String password) throws ClassNotFoundException, SQLException{
        String requete = "Select * from User Where id = '" + user + "' and password = '" + password + "';";
        System.out.println(requete);
        Statement statement = getConnexion().createStatement();
        ResultSet resultat = statement.executeQuery(requete);
        return resultat.next();
    }
    
}

La requête utilisé pour extraire l'utilisateur est une requête simple. Si les paramètres "user" et "password" ont les valeurs "admin" et "admin, la requête générée sera :

Select * from User Where id = 'admin' and password = 'admin';

La deuxième classe est une JFrame qui permet la saisie des deux valeurs à passer pour la méthode "verifierUSer()" citée ci-dessus. Elle affiche un message de confirmation en cas de réussite de l'authentification et un message d'erreur dans le cas contraire :

   private void btnLogInActionPerformed(ActionEvent evt) {                                         
        try {
            if(AccesBDD.verifierUser(txtUser.getText(), txtPassword.getText())){
                JOptionPane.showMessageDialog(this, "Login avec succès", "Succès", JOptionPane.INFORMATION_MESSAGE);
            }else{
                JOptionPane.showMessageDialog(this, "Login a échoué", "Echec", JOptionPane.ERROR_MESSAGE);
            }
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }              

La saisie des paramètres corrects

Le message au cas du succès

Saisie des paramètres incorrects

Message en cas d'échec
Jusqu'à maintenant, l'exécution semble normale parce que les valeur saisies ont été prévues par le développeur. LE problème se pose lorsqu'on commence à saisir non pas des valeurs simples mais des codes SQL. Par exemple, la saisie de la valeur suivante comme mot de passe :
' or '1' = '1
Permet de s'authentifier avec succès :



Pour comprendre la raison, il faut jeter un coup d'oeil sur la requête SQL générée après la saisie de cette valeur :


Select * from User Where id = '' and password = '' or '1' = '1';

Ainsi, al avleur saisie a permis de modifier le code de la requête et de changer sa signification.

Ce type d'attaque nécessite la vérification de toutes les données saisies comme il est possible d'utiliser des méthodes de connexion à la base plus sûres telles que l'utilisation de preparedStatement au lieu de Statement sous Java.


    public static boolean verifierUserSecurisee(String user, String password) throws ClassNotFoundException, SQLException{
        String requete = "Select * from User Where id = ? and password = ?;";
        PreparedStatement pstatement = getConnexion().prepareStatement(requete);
        pstatement.setString(1, user);
        pstatement.setString(2, password);
        ResultSet resultat = pstatement.executeQuery();
        return resultat.next();
    }

Aucun commentaire: