Domanda

Stavo cercando un algoritmo efficace che possa darmi un'idea precisa di quanto sia forte una password.

Ho scoperto che diversi siti Web diversi utilizzano algoritmi diversi man mano che ottengo livelli di protezione della password diversi su siti Web diversi.

È stato utile?

Soluzione

Questo è cresciuto fino al mio dump generale del cervello delle migliori pratiche per lavorare con le password in PHP / MySQL. Le idee presentate qui non sono generalmente mie, ma la migliore di ciò che ho trovato fino ad oggi.


Assicurati di utilizzare SSL per tutte le operazioni che coinvolgono le informazioni dell'utente. Tutte le pagine che coinvolgono questi moduli devono verificare che vengano chiamate tramite HTTPS e rifiutarsi di funzionare diversamente.

Puoi eliminare la maggior parte degli attacchi semplicemente limitando il numero di accessi non riusciti consentiti.

Consenti password relativamente deboli, ma memorizza il numero di accessi non riusciti per utente e richiedi una verifica captcha o password via e-mail se la superi. Ho impostato i miei errori massimi su 5.

La presentazione degli errori di accesso all'utente deve essere attentamente studiata per non fornire informazioni agli aggressori. Un accesso non riuscito a causa di un utente inesistente dovrebbe restituire lo stesso messaggio di un accesso non riuscito a causa di una password errata. Fornire un messaggio diverso consentirà agli aggressori di determinare accessi utente validi.

Assicurati inoltre di restituire esattamente lo stesso messaggio in caso di errore per troppi accessi con una password valida e un errore con troppi accessi e una password errata. Fornire un messaggio diverso consentirà agli aggressori di determinare password utente valide. Un buon numero di utenti, quando sono costretti a reimpostare la propria password, la riporterà semplicemente a quello che era.

Purtroppo non è pratico limitare il numero di accessi consentiti per indirizzo IP. Diversi provider come AOL e la maggior parte delle aziende delegano le loro richieste web. L'imposizione di questo limite eliminerà efficacemente questi utenti.


Ho trovato che il controllo delle parole del dizionario prima di inviare sia inefficace in quanto è necessario inviare un dizionario al client in javascript o inviare una richiesta Ajax per la modifica del campo. L'ho fatto per un po 'e ha funzionato bene, ma non mi è piaciuto il traffico che ha generato.

Controllare le password intrinsecamente deboli meno le parole del dizionario È lato client pratico con un semplice javascript.

Dopo l'invio, controllo le parole del dizionario e il nome utente contenente la password e viceversa lato server. I dizionari molto buoni sono facilmente scaricabili e il test contro di essi è semplice. Un problema qui è che per testare una parola del dizionario, è necessario inviare una query sul database, che contiene nuovamente la password. Il modo in cui mi sono comportato è stato quello di crittografare prima il mio dizionario con una semplice crittografia e terminare SALT posizionato e quindi verificare la password crittografata. Non ideale, ma migliore del semplice testo e solo sul filo per le persone sulle tue macchine fisiche e sottorete.

Una volta che sei soddisfatto della password, l'hanno scelta per crittografarla prima con PHP, quindi archiviarla. Anche la seguente funzione di crittografia della password non è una mia idea, ma risolve una serie di problemi. La crittografia all'interno di PHP impedisce alle persone su un server condiviso di intercettare le password non crittografate. Aggiungendo qualcosa per utente che non cambierà (io uso l'e-mail in quanto questo è il nome utente per i miei siti) e aggiungi un hash (SALT è una stringa breve e costante che cambio per sito) aumenta la resistenza agli attacchi. Poiché il SALT si trova all'interno della password e la password può essere di qualsiasi lunghezza, diventa quasi impossibile attaccarlo con una tabella arcobaleno. In alternativa, ciò significa anche che le persone non possono modificare la propria e-mail e non è possibile modificare il SALT senza invalidare la password di tutti.

MODIFICA: ora consiglierei di usare PhPass invece del mio roll qui la tua funzione, o semplicemente dimentica del tutto il login degli utenti e usa OpenID invece.

function password_crypt($email,$toHash) {
  $password = str_split($toHash,(strlen($toHash)/2)+1);
  return hash('sha256', $email.$password[0].SALT.$password[1]); 
}

Il mio contatore di password lato client Jqueryish. L'obiettivo dovrebbe essere un div. La sua larghezza cambierà tra 0 e 100 e il colore di sfondo cambierà in base alle classi indicate nello script. Ancora una volta per lo più rubato da altre cose che ho trovato:

$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
    repeated=true;
    for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
        repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
    if (j<pLen) repeated=false;
    if (repeated) {
        i+=pLen-1;
        repeated=false;
    }
    else {
        res+=str.charAt(i);
    };
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) { 
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
  return true;
} 
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/))  score += 5; 

//password has 2 symbols
if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5; 

//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/))  score += 10; 

//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/))  score += 15; 
//
//password has number and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/))  score += 15; 

//password has char and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/))  score += 15; 

//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) )  score -= 10; 

//verifing 0 < score < 100
score = score * 2;
if ( score < 0 )  score = 0;
if ( score > 100 )  score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50  ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};

Altri suggerimenti

Fondamentalmente vuoi prevenire i principali tipi di attacchi

  • Attacchi a dizionario
  • Attacchi di forza bruta

Per evitare il primo, si desidera considerare deboli le password contenenti parole comuni. Per evitare il secondo, si desidera incoraggiare password di lunghezza ragionevole (8+ caratteri è comune) e con un set di caratteri ragionevolmente grande (includere lettere, numeri e caratteri speciali). Se si considerano le lettere minuscole e maiuscole come diverse, ciò aumenta notevolmente il set di caratteri. Tuttavia, ciò crea un problema di usabilità per alcune comunità di utenti, quindi è necessario bilanciare tale considerazione.

Una rapida ricerca su Google ha rivelato soluzioni che tengono conto degli attacchi di forza bruta (password complessa) ma non degli attacchi del dizionario. PHP Misuratore di forza password da questo elenco di controlli di forza esegue il controllo lato server, quindi potrebbe essere esteso per controllare un dizionario.

EDIT:

A proposito ... dovresti anche limitare il numero di tentativi di accesso per utente. Ciò renderà meno probabili entrambi i tipi di attacchi. Efficace ma non user-friendly è bloccare un account dopo X tentativi errati e richiedere una reimpostazione della password. Più facile da usare ma più sforzo è quello di limitare il tempo tra i tentativi di accesso. Puoi anche richiedere CAPTCHA dopo i primi tentativi di accesso (che è qualcosa che Stack Overflow richiede dopo troppe modifiche o per utenti molto nuovi).

Fondamentalmente probabilmente si desidera utilizzare le espressioni regolari per convalidare la lunghezza e la complessità della password.

Un buon esempio in tal senso utilizzando JavaScript è disponibile qui:

http://marketingtechblog.com/programming/javascript-password-strength/

Come sottolineato da Daren Schwenke, faresti meglio a lavorare tu stesso sulla sicurezza e non metterlo nelle mani dell'utente .

Ma è utile fornire alcuni suggerimenti all'utente su quanto è forte la sua password, perché il modo migliore per ottenere una password è ancora social socialing .

Quindi puoi hackerare un piccolo script lato client che controlla la forza della password dell'utente come indicatore di cortesia, in tempo reale. Non blocca nulla, ma gli dà una buona sensazione di calore quando diventa verde :-)

Fondamentalmente ciò che devi controllare è il senso comune: controlla se la password contiene lettere, numeri e caratteri non alfabetici, in una quantità ragionevole.

Puoi hackerare il tuo algo molto facilmente: basta segnare 10/10:

  • 0 è una password di lunghezza zero;
  • +2 per ogni 8 caratteri nella password (15 dovrebbe essere una lunghezza sicura);
  • +1 per l'uso di una lettera, +2 per l'uso di 2 lettere;
  • +1 per l'uso di un numero, +2 per l'uso di 2 numeri;
  • +1 per l'uso di caratteri non alfabetici, +2 per 2.

Non è necessario controllare le password divine (ci sono lettere maiuscole, dove sono posizionati i caratteri speciali, ecc.), i tuoi utenti non sono nel settore bancario / militare / dei servizi segreti / mensili, vero?

Puoi codificarlo in un'ora senza pazze abilità javascript.

E comunque, convalida la password e sposta tutto il codice di sicurezza sul lato server. Se è possibile delegare l'autenticazione (ad esempio: ID aperto), ancora meglio.

Non riesco a pensare a un algoritmo specifico per verificare la forza di una password. Ciò che facciamo è definire diversi criteri e quando la password rispetta un criterio, aggiungiamo 1 al suo punteggio. Quando la password raggiunge una soglia, la password è forte. Altrimenti è debole.

Puoi definire molti livelli diversi di forza se con un valore diverso, oppure puoi definire valori diversi per un criterio specifico. Ad esempio, se una password ha 5 caratteri, si aggiunge 1, ma se ne ha 10, si aggiunge 2.

ecco un elenco di criteri da verificare

Lunghezza (da 8 a 12 è ok, più è meglio) Contiene lettere minuscole Contiene lettere maiuscole La lettera maiuscola NON è la prima. Contiene il numero Contiene simboli l'ultimo personaggio NON è un simbolo simile all'uomo (es:. o!) Non sembra una parola dizione. Qualche saggia password crack contiene una libreria di sostituti di parole e lettere (come Library - > L1br @ ry)

Spero che ti aiuti.

Non roll-your-own!

Gli esperti di crittografia scoraggiano la crittografia roll-your-own per motivi che dovrebbero essere ovvi.

Per le stesse ragioni, non si dovrebbe tentare di proporre la propria soluzione al problema di misurare la forza di una password; è molto un problema crittografico.

Non entrare nel brutto affare di creare qualche espressione regolare voluminosa per questo scopo; probabilmente non riuscirai a tenere conto di diversi fattori che influenzano la forza complessiva di una password.

È un problema difficile

Esistono notevoli difficoltà inerenti al problema di misurare la forza di una password. Più ricerche eseguo su questo argomento, più mi rendo conto che si tratta di un "unidirezionale" problema; cioè non si può misurare la "difficoltà" (costo di calcolo) per decifrare una password in modo efficiente . Piuttosto, è più efficiente fornire requisiti di complessità e misurare la capacità della password di soddisfarli.

Se consideriamo il problema logicamente, un "indice di crackabilità" non ha molto senso, comodo come sembra. Ci sono così tanti fattori che guidano il calcolo, la maggior parte dei quali riguarda le risorse computazionali dedicate al processo di cracking, in modo da essere poco pratico.

Immagina di mettere John the Ripper (o uno strumento simile) contro la password in questione; potrebbero essere necessari giorni per decifrare una password decente, mesi per decifrare una buona password e fino a quando il sole brucia per decifrare una password eccezionale. Questo non è un mezzo pratico per misurare la sicurezza della password.

Affrontare il problema dall'altra direzione è molto più gestibile: se forniamo una serie di requisiti di complessità, è possibile giudicare molto rapidamente la relativa forza di una password. Ovviamente, i requisiti di complessità forniti devono evolversi nel tempo, ma ci sono molte meno variabili di cui tenere conto se affrontiamo il problema in questo modo.

Una soluzione praticabile

Esiste un'utilità autonoma disponibile da Openwall intitolata passwdqc (presumibilmente , che sta per Controllo qualità password ). Lo sviluppatore di Openwall, Solar Designer, sembra essere un esperto di crittografia in buona fede (i suoi lavori parlano da soli), quindi è qualificato per creare tale strumento.

Per il mio particolare caso d'uso, questa è una soluzione molto più interessante rispetto all'utilizzo di uno snippet JavaScript mal concepito che vive in un angolo buio del Web.

Stabilire parametri per le tue esigenze particolari è la parte più difficile. L'implementazione è la parte facile.

Un esempio pratico

Offro una semplice implementazione in PHP per fornire un punto di partenza. Si applicano le dichiarazioni di non responsabilità standard.

Questo esempio presuppone che stiamo fornendo un intero elenco di password allo script PHP. Inutile dire che se lo si fa con password reali (ad esempio, quelle che sono state scaricate da un gestore di password), si dovrebbe prestare estrema attenzione riguardo alla gestione delle password. Scrivere semplicemente il dump della password non crittografato su disco mette a rischio la sicurezza delle tue password!

passwords.csv :

"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"

password check.php :

<?php

//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692

$csv = array_map('str_getcsv', file('passwords.csv'), [',']);

array_walk($csv, function(&$a) use ($csv) {
    $a = array_combine($csv[0], $a);
});

//Remove column header.

array_shift($csv);

//Define report column headers.

$results[] = [
    'Title',
    'Result',
    'Exit Code',
];

$i = 1;

foreach ($csv as $p) {
    $row['title'] = $p['Title'];

    //If the value contains a space, it's considered a passphrase.

    $isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;

    $cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';

    if ($isPassphrase) {
        $cmd .= ' passphrase=3';
    }
    else {
        $cmd .= ' passphrase=0';
    }

    $output = null;
    $exitCode = null;

    $stdOut = exec($cmd, $output, $exitCode);

    //Exit code 0 represents an unacceptable password (not an error).
    //Exit code 1 represents an acceptable password (it meets the criteria).

    if ($exitCode === 0 || $exitCode === 1) {
        $row['result'] = trim($stdOut);
        $row['exitCode'] = $exitCode;
    }
    else {
        $row['result'] = 'An error occurred while calling pwqcheck';
        $row['exitCode'] = null;
    }

    $results[$i] = $row;

    $i++;
}

$reportFile = 'report.csv';

$fp = @fopen($reportFile, 'w');

if ($fp !== false) {
    foreach ($results as $p) {
        fputcsv($fp, $p);
    }

    fclose($fp);
}
else {
    die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}

exit;

risultante report.csv :

Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0

Wrapping-up

Devo ancora trovare una soluzione più completa sul Web; inutile dirlo, accolgo con favore qualsiasi altra raccomandazione.

Ovviamente, questo approccio non è ideale per alcuni casi d'uso (ad es. un "misuratore di forza della password" implementato "lato client"). Tuttavia, sarebbe banale effettuare una chiamata AJAX a una risorsa lato server che restituisce una risposta pass / fail utilizzando l'approccio sopra descritto, ma tale approccio dovrebbe assumere il potenziale di abuso (ad esempio, attacchi DoS) e richiederebbe comunicazione sicura tra client e server, nonché accettazione dei rischi associati alla trasmissione della password senza hash.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top