Tôi sẽ đăng câu trả lời vì một số câu trả lời hiện có gần nhưng có một trong:
- một không gian ký tự nhỏ hơn bạn muốn để việc bắt buộc dễ dàng hơn hoặc mật khẩu phải dài hơn cho cùng một entropy
- một RNG không được coi là an toàn về mật mã
- một yêu cầu đối với một số thư viện của bên thứ 3 và tôi nghĩ sẽ rất thú vị khi thể hiện những gì có thể cần để tự làm
Câu trả lời này sẽ tránh được count/strlen
vấn đề vì tính bảo mật của mật khẩu được tạo, ít nhất là IMHO, vượt xa cách bạn đến đó. Tôi cũng sẽ giả sử PHP> 5.3.0.
Hãy chia vấn đề thành các phần cấu thành:
- sử dụng một số nguồn ngẫu nhiên an toàn để có được dữ liệu ngẫu nhiên
- sử dụng dữ liệu đó và biểu diễn nó dưới dạng một chuỗi có thể in
Đối với phần đầu tiên, PHP> 5.3.0 cung cấp hàm openssl_random_pseudo_bytes
. Lưu ý rằng trong khi hầu hết các hệ thống sử dụng thuật toán mã hóa mạnh, bạn phải kiểm tra để chúng tôi sử dụng trình bao bọc:
/**
* @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;
}
Đối với phần thứ hai, chúng ta sẽ sử dụng base64_encode
vì nó lấy một chuỗi byte và sẽ tạo ra một chuỗi các ký tự có bảng chữ cái rất gần với ký tự được chỉ định trong câu hỏi ban đầu. Nếu chúng tôi không phiền +
, /
và các =
ký tự xuất hiện trong chuỗi cuối cùng và chúng tôi muốn có kết quả $n
dài ít nhất là các ký tự, chúng tôi chỉ có thể sử dụng:
base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
Các 3/4
yếu tố là do thực tế rằng base64 mã hóa kết quả trong một chuỗi có chiều dài ít nhất một phần ba lớn hơn chuỗi byte. Kết quả sẽ chính xác $n
là bội số của 4 và tối đa 3 ký tự nếu không. Vì các ký tự phụ chủ yếu là ký tự đệm =
, nếu vì lý do nào đó chúng tôi có một ràng buộc rằng mật khẩu có độ dài chính xác, thì chúng tôi có thể cắt nó theo độ dài chúng tôi muốn. Điều này đặc biệt bởi vì đối với một $n
mật khẩu nhất định , tất cả mật khẩu sẽ kết thúc với cùng một số trong số này, do đó, kẻ tấn công có quyền truy cập vào mật khẩu kết quả, sẽ có ít hơn 2 ký tự để đoán.
Đối với tín dụng bổ sung, nếu chúng tôi muốn đáp ứng thông số kỹ thuật chính xác như trong câu hỏi của OP thì chúng tôi sẽ phải làm thêm một chút. Tôi sẽ từ bỏ cách tiếp cận chuyển đổi cơ sở ở đây và đi với một cách nhanh chóng và bẩn thỉu. Cả hai cần tạo ra nhiều tính ngẫu nhiên hơn dù sao sẽ được sử dụng trong kết quả vì bảng chữ cái dài 62 mục.
Đối với các ký tự phụ trong kết quả, chúng ta chỉ cần loại bỏ chúng khỏi chuỗi kết quả. Nếu chúng ta bắt đầu với 8 byte trong chuỗi byte của mình, thì có tới khoảng 25% các ký tự base64 sẽ là các ký tự "không mong muốn" này, do đó, việc loại bỏ các ký tự này sẽ dẫn đến một chuỗi không ngắn hơn OP muốn. Sau đó, chúng ta có thể cắt ngắn nó để đi xuống độ dài chính xác:
$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
Nếu bạn tạo mật khẩu dài hơn, ký tự đệm =
tạo thành một tỷ lệ nhỏ hơn và nhỏ hơn của kết quả trung gian để bạn có thể thực hiện một cách tiếp cận gọn gàng hơn, nếu việc sử dụng nhóm entropy được sử dụng cho PRNG là một mối quan tâm.