Pubblicherò una risposta perché alcune delle risposte esistenti sono vicine ma hanno una di:
- uno spazio di caratteri più piccolo di quello che volevi, in modo che sia la forza bruta sia più facile o la password debba essere più lunga per la stessa entropia
- un RNG che non è considerato sicuro dal punto di vista crittografico
- un requisito per alcune librerie di terze parti e ho pensato che potrebbe essere interessante mostrare cosa potrebbe essere necessario per farlo da soli
Questa risposta aggirerà il count/strlen
problema poiché la sicurezza della password generata, almeno IMHO, trascende il modo in cui ci si arriva. Presumo anche PHP> 5.3.0.
Suddividiamo il problema nelle parti costituenti che sono:
- utilizzare una fonte sicura di casualità per ottenere dati casuali
- usa quei dati e rappresentali come una stringa stampabile
Per la prima parte, PHP> 5.3.0 fornisce la funzione openssl_random_pseudo_bytes
. Si noti che mentre la maggior parte dei sistemi utilizza un algoritmo crittograficamente forte, è necessario verificare quindi utilizzeremo un wrapper:
/**
* @param int $length
*/
function strong_random_bytes($length)
{
$strong = false; // Flag for whether a strong algorithm was used
$bytes = openssl_random_pseudo_bytes($length, $strong);
if ( ! $strong)
{
// System did not use a cryptographically strong algorithm
throw new Exception('Strong algorithm not available for PRNG.');
}
return $bytes;
}
Per la seconda parte, useremo base64_encode
poiché richiede una stringa di byte e produrrà una serie di caratteri che hanno un alfabeto molto vicino a quello specificato nella domanda originale. Se non ci dispiace avere +
, /
e i =
caratteri compaiono nella stringa finale e vogliamo un risultato lungo almeno $n
caratteri, potremmo semplicemente usare:
base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
Il 3/4
fattore è dovuto al fatto che la codifica base64 genera una stringa che ha una lunghezza di almeno un terzo maggiore della stringa di byte. Il risultato sarà esatto per $n
essere un multiplo di 4 e fino a 3 caratteri più a lungo altrimenti. Poiché i caratteri extra sono prevalentemente il carattere di riempimento =
, se per qualche motivo avessimo il vincolo che la password fosse una lunghezza esatta, allora possiamo troncarla nella lunghezza che vogliamo. Ciò è dovuto in particolare al fatto che, per un dato periodo $n
, tutte le password terminerebbero con lo stesso numero di queste, in modo che un utente malintenzionato che aveva accesso a una password risultante avrebbe un massimo di 2 caratteri da indovinare.
Per ulteriore credito, se volessimo soddisfare le specifiche esatte come nella domanda del PO, dovremmo fare un po 'più di lavoro. Ho intenzione di rinunciare all'approccio di conversione di base qui e andare con uno veloce e sporco. Entrambi devono generare una casualità maggiore di quella che verrà comunque utilizzata nel risultato a causa dell'alfabeto lungo di 62 voci.
Per i caratteri extra nel risultato, possiamo semplicemente scartarli dalla stringa risultante. Se iniziamo con 8 byte nella nostra stringa di byte, fino a circa il 25% dei caratteri base64 sarebbero questi caratteri "indesiderabili", in modo che scartare semplicemente questi caratteri si traduca in una stringa non più corta dell'OP desiderato. Quindi possiamo semplicemente troncarlo per arrivare alla lunghezza esatta:
$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
Se si generano password più lunghe, il carattere di riempimento =
costituisce una proporzione sempre più piccola del risultato intermedio in modo da poter implementare un approccio più snello, se il drenaggio del pool di entropia utilizzato per il PRNG è un problema.