Aller au contenu

LDAP Injection

Injection LDAP

Description

L'injection LDAP (LDAP Injection) consiste à exploiter des applications web qui génèrent des instructions LDAP à l'aide d'entrées utilisateur. Dans les cas où une application ne parvient pas à nettoyer correctement les entrées utilisateur, les attaquants peuvent manipuler les instructions LDAP en injectant des mots-clés spéciaux dans la requête, une attaque similaire à l'injection SQL.

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;

public class LDAPSearchExample {

    public static void main(String[] args) {
        // Entrée de l'utilisateur (remplacez ceci par votre propre mécanisme d'entrée)
        String userInput = "username"; // Entrée fournie par l'utilisateur

        // Paramètres de connexion LDAP
        String ldapURL = "ldap://your-ldap-server:389";
        String baseDN = "ou=users,dc=example,dc=com"; // Remplacez par votre DN de base

        // Configuration de l'environnement pour créer le contexte initial
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, ldapURL);

        try {
            // Créer le contexte initial
            LdapContext ctx = new InitialLdapContext(env, null);

            // Spécifier le filtre de recherche
            String searchFilter = "(uid=" + userInput + ")";

            // Définir les contrôles de recherche
            SearchControls searchControls = new SearchControls();
            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            // Effectuer la recherche
            NamingEnumeration<SearchResult> results = ctx.search(baseDN, searchFilter, searchControls);

            // Parcourir les résultats de la recherche
            while (results.hasMore()) {
                SearchResult result = results.next();
                // Traiter le résultat selon les besoins (par ex., afficher les attributs)
                Attributes attributes = result.getAttributes();
                System.out.println("User found: " + attributes.get("cn").get());
            }

            // Fermer le contexte
            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
const ldap = require('ldapjs');

const userInput = 'user123';
const ldapQuery = `(uid=${userInput})`; // Vulnérable à l'injection LDAP

const client = ldap.createClient({
    url: 'ldap://example.com:389'
});

client.search('ou=users,dc=example,dc=com', {
    filter: ldapQuery,
}, (err, res) => {
    // Traiter les résultats de la recherche
    res.on('searchEntry', (entry) => {
        // Gérer l'entrée
    });

    res.on('error', (err) => {
        console.error('Error:', err.message);
    });
});
<?php
// Code PHP vulnérable avec injection LDAP
$userInput = 'user123';
$ldapQuery = "(uid=$userInput)"; // Vulnérable à l'injection LDAP

$ldapconn = ldap_connect("ldap://example.com");
ldap_bind($ldapconn, "cn=admin,dc=example,dc=com", "adminpassword");

$result = ldap_search($ldapconn, "ou=users,dc=example,dc=com", $ldapQuery);
$entries = ldap_get_entries($ldapconn, $result);

// Traiter les résultats de la recherche
foreach ($entries as $entry) {
    // Gérer l'entrée
}

ldap_close($ldapconn);
?>

Recommandation

Pour atténuer le risque d'injection LDAP, prenez en compte les recommandations suivantes :

  • Requêtes paramétrées : Utilisez des requêtes paramétrées au lieu de la concaténation directe pour construire des requêtes LDAP.

  • Validation / Nettoyage des entrées : Implémentez la validation des entrées pour vous assurer que les entrées de l'utilisateur respectent les formats attendus et ne contiennent pas de caractères spéciaux susceptibles de modifier les requêtes LDAP. Nettoyez également les entrées de l'utilisateur à l'aide de méthodes telles que LdapFilterEncode.

  • Utiliser des frameworks qui protègent automatiquement contre l'injection LDAP : Utilisez des API ou des bibliothèques de requêtes LDAP bien établies et sécurisées qui encodent automatiquement les entrées de l'utilisateur lors de la création de requêtes LDAP.

  • Activer l'authentification Bind : L'utilisation de l'authentification bind limitera la surface d'attaque du serveur LDAP si un attaquant parvient à réaliser une injection LDAP.

  • Moindre privilège : Le principe de moindre privilège est une règle de base conseillée pour minimiser l'impact des services compromis.

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;

public class LDAPSearchMitigatedExample {

    public static void main(String[] args) {
        // Entrée de l'utilisateur (remplacez ceci par votre propre mécanisme d'entrée)
        String userInput = "username"; // Entrée fournie par l'utilisateur

        // Paramètres de connexion LDAP
        String ldapURL = "ldap://your-ldap-server:389";
        String baseDN = "ou=users,dc=example,dc=com"; // Remplacez par votre DN de base

        // Configuration de l'environnement pour créer le contexte initial
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, ldapURL);

        try {
            // Créer le contexte initial
            LdapContext ctx = new InitialLdapContext(env, null);

            // Utiliser une requête paramétrée pour éviter l'injection LDAP
            String searchFilter = "(uid={0})";
            Object[] filterArgs = {userInput};

            // Définir les contrôles de recherche
            SearchControls searchControls = new SearchControls();
            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            // Effectuer la recherche
            NamingEnumeration<SearchResult> results = ctx.search(baseDN, searchFilter, filterArgs, searchControls);

            // Parcourir les résultats de la recherche
            while (results.hasMore()) {
                SearchResult result = results.next();
                // Traiter le résultat selon les besoins (par ex., afficher les attributs)
                Attributes attributes = result.getAttributes();
                System.out.println("User found: " + attributes.get("cn").get());
            }

            // Fermer le contexte
            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
// Code JavaScript atténué avec validation des entrées et requête paramétrée
const ldap = require('ldapjs');

const userInput = 'user123';
// Effectuer la validation des entrées pour s'assurer que userInput est sûr pour la requête LDAP
if (isValidUserInput(userInput)) {
    const ldapQuery = `(uid=${userInput})`;
    const client = ldap.createClient({
        url: 'ldap://example.com:389'
    });

    client.search('ou=users,dc=example,dc=com', {
        filter: ldapQuery,
    }, (err, res) => {
        // Traiter les résultats de la recherche
        res.on('searchEntry', (entry) => {
            // Gérer l'entrée
        });

        res.on('error', (err) => {
            console.error('Error:', err.message);
        });
    });
} else {
    console.log('Invalid userInput');
}

function isValidUserInput(userInput) {
    // Implémenter une logique de validation des entrées appropriée
    // Par exemple, vérifier les caractères autorisés, la longueur, etc.
    return /^[a-zA-Z0-9]+$/.test(userInput);
}
<?php
// Code PHP atténué avec validation des entrées et requête paramétrée
$userInput = 'user123';
// Effectuer la validation des entrées pour s'assurer que userInput est sûr pour la requête LDAP
if (isValidUserInput($userInput)) {
    $ldapQuery = "(uid=$userInput)";
    $ldapconn = ldap_connect("ldap://example.com");
    ldap_bind($ldapconn, "cn=admin,dc=example,dc=com");

    $result = ldap_search($ldapconn, "ou=users,dc=example,dc=com", $ldapQuery);
    $entries = ldap_get_entries($ldapconn, $result);

    // Traiter les résultats de la recherche
    foreach ($entries as $entry) {
        // Gérer l'entrée
    }

    ldap_close($ldapconn);
} else {
    echo 'Invalid userInput';
}

function isValidUserInput($userInput) {
    // Implémenter une logique de validation des entrées appropriée
    // Par exemple, vérifier les caractères autorisés, la longueur, etc.
    return preg_match('/^[a-zA-Z0-9]+$/', $userInput);
}
?>

Liens

Normes

  • GDPR:
    • ART_25
    • ART_32
  • PCI_STANDARDS:
    • REQ_6_2
    • REQ_6_3
    • REQ_6_4
    • REQ_6_5
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
  • HIPAA_CONTROLS:
    • SECURITY212
    • SECURITY213
    • SECURITY255