Questo algoritmo per una stringa casuale è crittograficamente sicuro?

4

Ho messo insieme questo algoritmo (se può essere chiamato così) da vari bit di codice che ho visto online, e mi chiedo quanto sia sicuro crittograficamente. È usato per generare password:

function seed_random() {
    $seed = crc32(uniqid(sha1(microtime(true) . getmypid()), true));
    mt_srand($seed);
    $n = mt_rand(1, 200);
    for ($i = 0; $i < $n; $i++) {
        mt_rand();
    }
}

function mt_rand_custom($min, $max) {
    seed_random();
    return mt_rand($min, $max);
}

function random_password($length) {
    $legal_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'_!@#$^*?<>[]{}~(),";
    seed_random();
    $command = 'head -c 500 /dev/urandom | tr -dc %s | head -c %s; echo';
    return shell_exec(sprintf($command, $legal_characters, $length));
}

function generate_password() {
    return random_password(10);
}

Conosco la sicurezza, soprattutto in un ambiente web, dipende da numerosi altri aspetti (ad esempio la password viene inviata tramite SSL) ma sono curioso di conoscere questo specifico insieme di funzioni. Grazie per il feedback.

EDIT: anche se l'esempio non usa più la funzione mt_random_custom, qualsiasi feedback su questo sarebbe molto apprezzato, quindi l'ho lasciato.

    
posta Ricardo Altamirano 12.08.2011 - 22:39
fonte

2 risposte

8

Nel tuo codice, hai per riempire $legal_characters con l'elenco di caratteri che accetti come parte di una password. Per esempio:.

$legal_characters = "abcdefghijklmnopqrstuvwxyz";

se desideri password composte da lettere minuscole (tutte lettere latine minuscole, ma nessun altro carattere e nessun accento).

Questo codice è un po 'strano; usa mt_rand() (un PRNG interno) seminato con l'ora corrente e l'ID di processo per ottenere la lunghezza della password, tra 15 e 60 caratteri. Quindi usa /dev/urandom per la password stessa, che è intelligente poiché mt_rand() non è crittograficamente sicuro (soprattutto perché l'ID di processo non è qualcosa che è molto segreto, e nemmeno l'ora corrente).

La generazione effettiva della password funziona così: produce 500 byte casuali (da /dev/urandom ), quindi rimuove tutti quelli che non sono nel set di caratteri accettati (che è il " tr "), e infine tronca nuovamente il sequenza di caratteri rimanente alla lunghezza desiderata. Questo processo genera sequenze casuali uniformemente, quindi va bene, e /dev/urandom è il PRNG appropriato per questo. Nota, tuttavia, alcuni avvertimenti:

  • Se il set di "caratteri legali" è piccolo, è possibile terminare con una password più breve. Per esempio. se si desiderano password con solo cifre da 1 a 6, in media ci saranno solo 12 byte corrispondenti nei 500 byte casuali. Il codice non ha sicurezza per "troppo breve". Se imposti $legal_characters su tutte le 26 lettere minuscole, allora circa 1 byte su 10 sarà legale (i valori dei byte vanno da 0 a 255 e 26/256 è vicino a 1/10) e, in media, "% co_de" % "parte produrrà circa 50 caratteri, ed è estremamente improbabile che ceda meno di 20. Comunque, questo è degno di nota e dovrebbe essere un failsafe.

  • Non ha molto senso avere una lunghezza variabile per la password. Se una password di lunghezza minima è accettabile per la sicurezza, allora tutte le password potrebbero avere quella lunghezza. E se è non accettabile, allora perché usi quella lunghezza minima? Faresti meglio ad usare una singola lunghezza fissa, piuttosto che un intervallo. Ciò ti consentirebbe di rimuovere tr e seed_random() , semplificando sostanzialmente il codice.

  • Una "password" è qualcosa che un essere umano sarà in grado di digitare un memo - quindi "parola". Memorizzerà una sequenza di 60 caratteri? Sono sicuro che esiste un nome medico per tale condizione.

  • È necessario impostare la lunghezza delle password su un valore "appropriato" in modo che l'entropia sia sufficientemente alta. Se ci sono x caratteri legali e la lunghezza della password è n , allora l'entropia sarà x n . Quale entropia è necessaria dipende dall'uso previsto. Per autenticare gli utenti su un sito Web, tramite un tunnel SSL appropriato, 2 40 ("40 bit" di entropia) è più che sufficiente, che si traduce in 9 lettere minuscole (26 9 è maggiore di 2 40 ).

  • Il codice usa unixisms ( mt_rand_custom() , /dev/urandom , head ...) e avrà difficoltà a funzionare su, ad esempio, un server Windows (e PHP runs su Windows, anche ).

Riepilogo: le password saranno forti, ma il codice è strano. Dovresti eliminare la lunghezza variabile, e se i tuoi utenti possono ingoiare password di 60 caratteri, ti preghiamo di congratularmi con loro per me. O vendili allo zoo. Ecco un codice semplificato che dovrebbe essere OK (attenzione: io non uso PHP quindi sto improvvisando qui):

function generate_password() {
    return shell_exec('head -c 500 /dev/urandom | tr -dc abcdefghijklmnopqrstuvwxyz | head -c 9; echo');
}
    
risposta data 12.08.2011 - 23:47
fonte
3

Analisi. Il tuo codice presenta alcune carenze:

  1. Uso di sistema da parte di Buggy (). Sospetto che il comando della shell non esegua ciò che desideri, perché non hai citato vari metacaratteri della shell. Invece di tr -dc %s , hai bisogno di tr -dc '%s' , quindi devi sfuggire alla citazione singola in $ legal_characters.

  2. Set di caratteri troppo ampio. Questo non genererà password che possono essere utilizzate in modo affidabile su tutti i siti. Molti siti impongono restrizioni su quali caratteri sono consentiti nelle password (ad es. Possono proibire virgolette singole). Di conseguenza, se utilizzi il tuo script in pratica, troverai che spesso genera password che alcuni siti rifiutano. Suggerirei di limitare la gamma di caratteri a a-zA-Z0-9, per la massima portabilità. Ciò non ridurrà in modo significativo l'entropia o la sicurezza della password, ma renderà il tuo script più utile.

  3. Codice guasto. hai un codice morto inutile ( mt_rand_custom() ). Quel codice è anche roba croccante e non sicuro per scopi crittografici, ma questa è una tangente: se vuoi chiedere di quel codice, mettilo in una domanda separata. Non inserire due domande in un solo post.

Soluzione. @ Il comando di shell su una riga di Thomas Pornin è molto meglio. Personalmente, lo migliorerei leggermente consentendo lettere maiuscole e numeri:

head -c 500 /dev/urandom | tr -dc a-zA-Z0-9 | head -c 12; echo

Questo è sufficiente per ottenere 71 bit di entropia, che dovrebbero essere più che sufficienti per una password del sito Web, e le password risultanti dovrebbero essere accettate da quasi tutti i siti web là fuori.

(Se vuoi una password più breve, sostituendo 12 di 10 ottieni una password di 10 caratteri con 59 bit di entropia, che è probabilmente ancora sufficiente per tutti gli scopi ragionevoli. 47 bit di entropia, che è anche probabilmente sufficiente, purché il database di hash delle password del sito Web non sia compromesso e per la maggior parte degli utenti non è la cosa principale di cui hanno bisogno di preoccuparsi. Una password specifica del sito di 8 caratteri è abbastanza buono da prevenire attacchi di indovinare la password online, che è probabilmente la minaccia più importante che la maggior parte degli utenti deve affrontare.)

    
risposta data 13.08.2011 - 03:19
fonte

Leggi altre domande sui tag