ฉันจะโพสต์คำตอบเพราะบางคำตอบที่มีอยู่ใกล้ แต่มีหนึ่งใน:
- พื้นที่อักขระที่เล็กกว่าที่คุณต้องการเพื่อให้การบังคับใช้เดรัจฉานง่ายขึ้นหรือรหัสผ่านต้องยาวขึ้นสำหรับเอนโทรปีเดียวกัน
- RNGว่าจะไม่ถือว่าเป็นที่เชื่อถือได้เข้ารหัส
- ข้อกำหนดสำหรับห้องสมุดบุคคลที่สามและฉันคิดว่ามันน่าสนใจที่จะแสดงสิ่งที่อาจต้องทำด้วยตัวเอง
คำตอบนี้จะหลีกเลี่ยงcount/strlen
ปัญหาเนื่องจากความปลอดภัยของรหัสผ่านที่สร้างขึ้นอย่างน้อย IMHO จะผ่านพ้นวิธีที่คุณได้รับ ฉันจะสมมติ PHP> 5.3.0 ด้วย
ลองแยกปัญหาออกเป็นส่วนต่างๆซึ่ง ได้แก่ :
- ใช้แหล่งความปลอดภัยของการสุ่มเพื่อรับข้อมูลแบบสุ่ม
- ใช้ข้อมูลนั้นและแสดงว่าเป็นสตริงที่พิมพ์ได้
สำหรับส่วนแรก, PHP> 5.3.0 openssl_random_pseudo_bytes
ให้ฟังก์ชั่น โปรดทราบว่าในขณะที่ระบบส่วนใหญ่ใช้อัลกอริทึมที่แข็งแกร่งของการเข้ารหัสคุณต้องตรวจสอบดังนั้นเราจะใช้กระดาษห่อ:
/**
* @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;
}
สำหรับส่วนที่สองเราจะใช้base64_encode
เนื่องจากใช้สตริงไบต์และจะสร้างชุดอักขระที่มีตัวอักษรใกล้เคียงกับที่ระบุในคำถามดั้งเดิมมาก ถ้าเราไม่ได้คิดมี+
, /
และ=
ตัวละครที่ปรากฏในสตริงสุดท้ายและเราต้องการผลอย่างน้อย$n
ตัวละครยาวเราก็สามารถใช้:
base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
3/4
ปัจจัยที่เป็นผลมาจากความจริงที่ว่า base64 เข้ารหัสผลลัพธ์ในสตริงที่มีความยาวอย่างน้อยหนึ่งในสามที่ใหญ่กว่าสตริงไบต์ที่ ผลลัพธ์จะถูกต้องสำหรับ$n
การเป็นทวีคูณของ 4 และสูงสุด 3 ตัวอักษรเป็นอย่างอื่น เนื่องจากอักขระพิเศษส่วนใหญ่เป็นอักขระการขยาย=
ถ้าเราด้วยเหตุผลบางอย่างมีข้อ จำกัด ว่ารหัสผ่านมีความยาวที่แน่นอนดังนั้นเราจึงสามารถตัดให้เหลือความยาวตามที่เราต้องการ โดยเฉพาะอย่างยิ่งเนื่องจากมีการกำหนด$n
รหัสผ่านทั้งหมดจะลงท้ายด้วยหมายเลขเดียวกันดังนั้นผู้โจมตีที่เข้าถึงรหัสผ่านผลลัพธ์จะมีอักขระน้อยกว่า 2 ตัวในการเดา
สำหรับเครดิตพิเศษถ้าเราต้องการพบสเป็คที่แน่นอนเหมือนในคำถามของ OP เราจะต้องทำงานเพิ่มอีกนิดหน่อย ฉันจะทิ้งวิธีการแปลงฐานที่นี่และไปด้วยวิธีที่รวดเร็วและสกปรก ทั้งคู่จำเป็นต้องสร้างการสุ่มมากขึ้นกว่าที่จะใช้ในผลลัพธ์ต่อไปเนื่องจากมีตัวอักษรยาว 62 รายการ
สำหรับอักขระพิเศษในผลลัพธ์เราสามารถทิ้งมันจากสตริงผลลัพธ์ ถ้าเราเริ่มต้นด้วย 8 ไบต์ในไบต์สตริงของเราแล้วมากถึง 25% ของอักขระเบส 64 จะเป็นอักขระ "ไม่พึงปรารถนา" เหล่านี้ดังนั้นการทิ้งอักขระเหล่านี้ส่งผลให้เกิดสตริงไม่สั้นกว่าที่ต้องการ จากนั้นเราสามารถตัดให้สั้นลงเพื่อให้ได้ความยาวที่แน่นอน:
$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
หากคุณสร้างรหัสผ่านที่ยาวขึ้นอักขระการขยาย=
จะสร้างสัดส่วนของผลกลางที่เล็กลงและเล็กลงเพื่อให้คุณสามารถใช้วิธีการแบบลีนได้ถ้าการระบายเอนโทรปีที่ใช้สำหรับ PRNG นั้นเป็นเรื่องที่น่ากังวล