2010-08-18 8 views
8

Stiamo costruendo un'applicazione che utilizza LDAP via php e ho avuto modo di pensare che c'è qualcosa che puoi fare con l'iniezione in LDAP e meglio ancora come si protegge contro le iniezioni LDAP?come proteggere contro LDAP Injection

risposta

9

Quando si costruisce filtri LDAP è necessario assicurarsi che il filtro valori vengono gestiti secondo le RFC2254:

Qualsiasi carattere di controllo con un codice ACII < 32 così come i personaggi con un significato speciale nei filtri LDAP " * "," (",") "e" \ "(la barra rovesciata) vengono convertiti nella rappresentazione di una barra rovesciata seguita da due cifre esadecimali che rappresentano il valore esadecimale del carattere.

Zend_Ldap per esempio utilizza le seguenti impostazioni

//[...] 
$val = str_replace(array('\\', '*', '(', ')'), array('\5c', '\2a', '\28', '\29'), $val); 
for ($i = 0; $i<strlen($val); $i++) { 
    $char = substr($val, $i, 1); 
    if (ord($char)<32) { 
     $hex = dechex(ord($char)); 
     if (strlen($hex) == 1) $hex = '0' . $hex; 
     $val = str_replace($char, '\\' . $hex, $val); 
    } 
} 
//[...] 
+1

Come posso disinfettare la password, poiché questi caratteri speciali sono consentiti? – Bastien974

+0

Questo è esattamente ciò che fa il codice sopra ... trasforma caratteri speciali e li codifica. È proprio come gli URL, immagina se qualcuno inserisce un parametro & in, così lo converti.Solo perché la password del browser/ldap è intelligente e tenta di correggere questo errore, non significa che sia ancora consentito inviare caratteri speciali. – TravisO

+0

Ho provato questa soluzione e non funziona correttamente. Ho dovuto sostituire una matrice di caratteri esadecimali con: array ("\ x5c", "\ x2a", "\ x28", "\ x29") per farlo funzionare. –

1

Nella maggior parte dei casi utilizza un account di sola lettura per LDAP. Poiché LDAP è scarso in scrittura, gli aggiornamenti si verificano solo in sezioni molto piccole dell'applicazione in cui è possibile utilizzare un altro account.

Anche in questo caso il linguaggio di query e la lingua di aggiornamento sono completamente separati.

Per evitare la visualizzazione di informazioni indesiderate, considerare tutti gli input dell'utente come contaminati e assicurarsi che i dati contaminati non vengano mai utilizzati prima di essere analizzati, puliti e correttamente fugati e copiati in una variabile pulita.

Analogamente si può prendere in considerazione solo il prelievo dei dati previsti dalla risposta e il ritorno per la visualizzazione.

+0

"Per la protezione contro la visualizzazione di informazioni indesiderate trattare tutto l'input dell'utente come contaminato e assicurarsi che i dati grezzi sono mai usato prima di essere analizzato, puliti e adeguatamente fuggì e copiato in un ambiente pulito variabile." come si fa a fare questa è la domanda? – Chris

2

Un elemento da considerare è che un binding LDAP con un nome utente (DN) ma nessuna password è considerata un binding anonimo. Pertanto, dovresti testare per vedere se le credenziali passate possono collegarsi tramite LDAP per convalidare l'utente, se passano una password vuota e tu l'hai passata così com'è, potresti lasciare qualcuno in modo errato.

+0

Grazie, questo capisco e non è davvero un'iniezione. – Chris

+0

@Chris: concordato, non proprio un'iniezione, ma all'interno dello stesso problema di base dello spazio. – geoffc

+1

Dopo aver letto questa risposta, ho immediatamente testato un'app in sviluppo e questo particolare problema non è stato rilevato, grazie! –

0
function ldap_quote($str) { 
    return str_replace(
      array('\\', ' ', '*', '(', ')'), 
      array('\\5c', '\\20', '\\2a', '\\28', '\\29'), 
      $str 
    ); 
} 
+4

Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo riguardo a _how_ e/o _why_ risolve il problema migliorerebbe il valore a lungo termine della risposta. –

1

In PHP 5.6+ si dovrebbe utilizzare la funzione ldap_escape per i valori di filtro e RDNs. Come ad esempio:

// Escaping an LDAP filter for ldap_search ... 
$username = ldap_escape($username, null, LDAP_ESCAPE_FILTER); 
$filter = "(sAMAccountName=$username)"; 

// Escaping a DN to be used in an ldap_add, or a rename... 
$rdn = ldap_escape('Smith, John', null, LDAP_ESCAPE_DN); 
$dn = "cn=$rdn,dc=example,dc=local"; 

Inoltre, se si sta accettando l'input dell'utente per i nomi degli attributi nelle ricerche si dovrebbe essere la validazione che si tratta di un OID accettabile o nome di attributo. È possibile farlo con una funzione come questa:

/** 
* Validate an attribute is an OID or a valid string attribute name. 
* 
* @param string 
* @return bool 
*/ 
function isValidAttributeFormat($value) 
{ 
    $matchOid = '/^[0-9]+(\.[0-9]+?)*?$/'; 
    $matchDescriptor = '/^\pL([\pL\pN-]+)?$/iu'; 

    return preg_match($matchOid, $value) 
     || preg_match($matchDescriptor, $value); 
} 

$attribute = 'sAMAccountName'; 
$value = 'foo'; 

if (!isValidAttributeFormat($attribute)) { 
    throw new \InvalidArgumentException(sprintf('Invalid attribute name: %s', $attribute)); 
} 

$value = ldap_escape($value, null, LDAP_ESCAPE_FILTER); 
$filter = "($attribute=$value)";