ปัญหานี้เป็นปัญหาของเอนโทรปี มาเริ่มกันเลย:
เอนโทรปีต่อตัวละคร
จำนวนบิตของเอนโทรปีต่อไบต์คือ:
- อักขระ Hex
- บิต: 4
- ค่า: 16
- เอนโทรปีใน 72 อักขระ: 288 บิต
- อัลฟา - ตัวเลข
- บิต: 6
- ค่านิยม: 62
- เอนโทรปีใน 72 Chars: 432 บิต
- สัญลักษณ์ "ทั่วไป"
- บิต: 6.5
- ค่านิยม: 94
- เอนโทรปีใน 72 อักขระ: 468 บิต
- ไบต์เต็ม
- บิต: 8
- ค่า: 255
- เอนโทรปีใน 72 Chars: 576 บิต
ดังนั้นวิธีการแสดงของเราขึ้นอยู่กับประเภทของตัวละครที่เราคาดหวัง
ปัญหาแรก
ปัญหาแรกกับรหัสของคุณคือขั้นตอนแฮช"pepper"ของคุณกำลังแสดงอักขระเลขฐานสิบหก (เนื่องจากhash_hmac()
ไม่ได้ตั้งค่าพารามิเตอร์ที่สี่เป็น)
ดังนั้นการแฮ็กพริกไทยของคุณจะทำให้คุณสามารถตัดเอนโทรปีสูงสุดที่มีให้กับรหัสผ่านได้อย่างมีประสิทธิภาพด้วยปัจจัย 2 (จาก 576 ถึง 288 บิตที่เป็นไปได้ )
ปัญหาที่สอง
อย่างไรก็ตามsha256
ให้256
บิตของเอนโทรปีในตอนแรกเท่านั้น ดังนั้นคุณจึงตัด 576 บิตที่เป็นไปได้ให้เหลือ 256 บิตได้อย่างมีประสิทธิภาพ ขั้นตอนแฮชของคุณ * ทันที * โดยคำจำกัดความมากจะสูญเสีย
เอนโทรปีอย่างน้อย 50% ที่เป็นไปได้ในรหัสผ่าน
คุณสามารถแก้ปัญหานี้ได้บางส่วนโดยเปลี่ยนไปใช้SHA512
ซึ่งคุณจะลดเอนโทรปีที่มีอยู่ได้ประมาณ 12% เท่านั้น แต่นั่นก็ยังคงเป็นความแตกต่างที่ไม่สำคัญ 12% นั้นลดจำนวนการเรียงสับเปลี่ยนลงหนึ่ง1.8e19
เท่า นั่นเป็นจำนวนมาก ... และนั่นคือปัจจัยที่ทำให้มันลดลง ...
ปัญหาพื้นฐาน
ปัญหาพื้นฐานคือรหัสผ่านมีสามประเภทที่ยาวเกิน 72 อักขระ ผลกระทบที่ระบบรูปแบบนี้มีต่อพวกเขาจะแตกต่างกันมาก:
หมายเหตุ: จากตรงนี้ฉันสมมติว่าเรากำลังเปรียบเทียบกับระบบพริกไทยที่ใช้SHA512
กับเอาต์พุตดิบ (ไม่ใช่ฐานสิบหก)
รหัสผ่านแบบสุ่มเอนโทรปีสูง
นี่คือผู้ใช้ของคุณที่ใช้ตัวสร้างรหัสผ่านซึ่งจะสร้างจำนวนเงินให้กับคีย์ขนาดใหญ่สำหรับรหัสผ่าน เป็นแบบสุ่ม (สร้างขึ้นไม่ใช่โดยมนุษย์เลือก) และมีเอนโทรปีสูงต่ออักขระ ประเภทเหล่านี้กำลังใช้ไฮไบต์ (อักขระ> 127) และอักขระควบคุมบางตัว
สำหรับกลุ่มนี้ฟังก์ชั่นการแปลงแป้นพิมพ์ของคุณจะมีนัยสำคัญbcrypt
ลดเอนโทรปีของพวกเขาเป็นใช้ได้
ให้ฉันพูดอีกครั้ง สำหรับผู้ใช้ที่กำลังใช้เอนโทรปีสูงรหัสผ่านที่ยาว, การแก้ปัญหาของคุณอย่างมีนัยสำคัญจะช่วยลดความแข็งแรงของรหัสผ่านของพวกเขาตามจำนวนเงินที่วัดได้ (เอนโทรปี 62 บิตหายไปสำหรับรหัสผ่าน 72 อักขระและอื่น ๆ สำหรับรหัสผ่านที่ยาวขึ้น)
รหัสผ่านสุ่มเอนโทรปีขนาดกลาง
กลุ่มนี้กำลังใช้รหัสผ่านที่มีสัญลักษณ์ทั่วไป แต่ไม่มีไบต์สูงหรืออักขระควบคุม นี่คือรหัสผ่านที่คุณสามารถพิมพ์ได้
สำหรับกลุ่มนี้คุณจะปลดล็อกเอนโทรปีเพิ่มเติมเล็กน้อย (ไม่ต้องสร้าง แต่อนุญาตให้เอนโทรปีเพิ่มขึ้นเพื่อให้พอดีกับรหัสผ่าน bcrypt) เมื่อฉันพูดเล็กน้อยฉันหมายถึงเล็กน้อย จุดคุ้มทุนเกิดขึ้นเมื่อคุณใช้ 512 บิตสูงสุดที่ SHA512 มี ดังนั้นจุดสูงสุดจึงอยู่ที่ 78 อักขระ
ให้ฉันพูดอีกครั้ง สำหรับรหัสผ่านคลาสนี้คุณสามารถจัดเก็บอักขระได้อีก 6 ตัวก่อนที่เอนโทรปีจะหมด
รหัสผ่านแบบไม่สุ่มเอนโทรปีต่ำ
นี่คือกลุ่มที่ใช้อักขระที่เป็นตัวเลขและตัวอักษรซึ่งอาจไม่ได้สร้างขึ้นแบบสุ่ม บางอย่างเช่นคำพูดในพระคัมภีร์หรือเช่นนั้น วลีเหล่านี้มีเอนโทรปีประมาณ 2.3 บิตต่ออักขระ
สำหรับกลุ่มนี้คุณสามารถปลดล็อกเอนโทรปีได้มากขึ้น (ไม่ได้สร้างขึ้น แต่อนุญาตให้ใส่รหัสผ่าน bcrypt ได้มากขึ้น) โดยการแฮช จุดคุ้มทุนอยู่ที่ประมาณ 223 อักขระก่อนที่คุณจะหมดเอนโทรปี
ค่อยว่ากันอีกที สำหรับรหัสผ่านประเภทนี้การแฮชล่วงหน้าช่วยเพิ่มความปลอดภัยอย่างมาก
กลับสู่โลกแห่งความจริง
การคำนวณเอนโทรปีประเภทนี้ไม่ได้มีความสำคัญมากนักในโลกแห่งความเป็นจริง สิ่งที่สำคัญคือการคาดเดาเอนโทรปี นั่นคือสิ่งที่ส่งผลโดยตรงต่อสิ่งที่ผู้โจมตีสามารถทำได้ นั่นคือสิ่งที่คุณต้องการเพิ่มสูงสุด
แม้ว่าจะมีงานวิจัยเพียงเล็กน้อยที่คาดเดาเอนโทรปีได้ แต่ก็มีบางประเด็นที่ฉันอยากจะชี้ให้เห็น
โอกาสของการสุ่มเดา 72 ตัวอักษรที่ถูกต้องในแถวเป็นอย่างยิ่งต่ำ คุณมีแนวโน้มที่จะชนะลอตเตอรี Powerball 21 ครั้งมากกว่าที่จะเกิดการปะทะกันนี้ ... นั่นคือจำนวนที่มากที่เรากำลังพูดถึง
แต่เราอาจไม่สะดุดกับมันในทางสถิติ ในกรณีของวลีโอกาสที่อักขระ 72 ตัวแรกจะเหมือนกันนั้นสูงกว่ารหัสผ่านแบบสุ่ม แต่ก็ยังต่ำอยู่เล็กน้อย (คุณมีแนวโน้มที่จะชนะลอตเตอรี Powerball มากกว่า 5 ครั้งโดยอิงจาก 2.3 บิตต่อตัวละคร)
ในทางปฏิบัติ
ในทางปฏิบัติมันไม่สำคัญจริงๆ โอกาสที่คนจะเดาอักขระ 72 ตัวแรกถูกต้องโดยที่ตัวหลังสร้างความแตกต่างอย่างมีนัยสำคัญนั้นต่ำมากจนไม่น่ากังวล ทำไม?
สมมุติว่าคุณกำลังใช้วลี หากบุคคลนั้นสามารถรับอักขระ 72 ตัวแรกได้ถูกต้องพวกเขาก็โชคดีจริงๆ (ไม่น่าจะเป็นไปได้) หรือเป็นวลีทั่วไป หากเป็นวลีทั่วไปตัวแปรเดียวคือระยะเวลาในการสร้าง
ลองมาเป็นตัวอย่าง ลองมาอ้างจากพระคัมภีร์ (เพียงเพราะเป็นแหล่งที่มาของข้อความขนาดยาวทั่วไปไม่ใช่ด้วยเหตุผลอื่นใด):
อย่าโลภบ้านของเพื่อนบ้าน อย่าโลภภรรยาของเพื่อนบ้านหรือทาสหรือสาวใช้ของเขาวัวหรือลาของเขาหรือสิ่งใด ๆ ที่เป็นของเพื่อนบ้านของคุณ
นั่นคือ 180 อักขระ ตัวละครที่ 73 คือในครั้งที่สองg
neighbor's
หากคุณเดาได้มากขนาดนั้นคุณอาจจะไม่หยุดnei
แต่จะดำเนินการต่อในส่วนที่เหลือ (เนื่องจากเป็นวิธีที่น่าจะใช้รหัสผ่าน) ดังนั้น "แฮช" ของคุณจึงไม่ได้เพิ่มมากนัก
BTW: ฉันไม่สนับสนุนการใช้คำพูดในพระคัมภีร์อย่างแน่นอน ในความเป็นจริงตรงกันข้ามแน่นอน
สรุป
คุณจะไม่ช่วยคนที่ใช้รหัสผ่านยาว ๆ ด้วยการแฮชก่อน บางกลุ่มคุณช่วยได้แน่นอน บางอย่างคุณสามารถทำร้ายได้แน่นอน
แต่ท้ายที่สุดแล้วไม่มีสิ่งใดที่สำคัญเกินไป ตัวเลขที่เราจะจัดการกับเป็นเพียงWAYสูงเกินไป ความแตกต่างของเอนโทรปีจะไม่มากนัก
คุณดีกว่าที่จะออกจาก bcrypt อย่างที่เป็นอยู่ คุณมีแนวโน้มที่จะทำลายการแฮช (แท้จริงแล้วคุณได้ทำไปแล้วและคุณไม่ใช่คนแรกหรือคนสุดท้ายที่ทำผิดพลาดนั้น) มากกว่าการโจมตีที่คุณพยายามป้องกันจะเกิดขึ้น
มุ่งเน้นไปที่การรักษาความปลอดภัยส่วนที่เหลือของไซต์ และเพิ่มเครื่องวัดเอนโทรปีของรหัสผ่านลงในช่องรหัสผ่านในการลงทะเบียนเพื่อระบุความแข็งแกร่งของรหัสผ่าน (และระบุว่ารหัสผ่านยาวเกินไปหรือไม่ซึ่งผู้ใช้อาจต้องการเปลี่ยน) ...
นั่นคือ 0.02 ดอลลาร์ของฉันเป็นอย่างน้อย (หรืออาจมากกว่า 0.02 ดอลลาร์) ...
เท่าที่ใช้พริกไทย "ลับ":
ไม่มีงานวิจัยเกี่ยวกับการป้อนฟังก์ชันแฮชลงใน bcrypt อย่างแท้จริง ดังนั้นจึงไม่มีความชัดเจนที่ดีที่สุดหากการป้อนแฮช "peppered" ลงใน bcrypt จะทำให้เกิดช่องโหว่ที่ไม่รู้จัก (เรารู้ว่าการทำเช่นhash1(hash2($value))
นี้อาจทำให้เกิดช่องโหว่ที่สำคัญเกี่ยวกับการต้านทานการชนและการโจมตีก่อนภาพ)
เมื่อพิจารณาว่าคุณกำลังพิจารณาจัดเก็บคีย์ลับ ("พริกไทย") อยู่แล้วทำไมไม่ใช้มันในแบบที่ศึกษาและเข้าใจกันดีล่ะ ทำไมไม่เข้ารหัสแฮชก่อนจัดเก็บ?
โดยทั่วไปหลังจากที่คุณแฮชรหัสผ่านแล้วให้ป้อนเอาต์พุตแฮชทั้งหมดลงในอัลกอริทึมการเข้ารหัสที่แข็งแกร่ง จากนั้นจัดเก็บผลลัพธ์ที่เข้ารหัส
ตอนนี้การโจมตีด้วย SQL-Injection จะไม่รั่วไหลอะไรที่เป็นประโยชน์เพราะพวกเขาไม่มีรหัสตัวเลข และหากกุญแจรั่วผู้โจมตีจะไม่ดีไปกว่าถ้าคุณใช้แฮชธรรมดา (ซึ่งสามารถพิสูจน์ได้บางอย่างที่มีพริกไทย
หมายเหตุ: หากคุณเลือกที่จะทำสิ่งนี้ให้ใช้ไลบรารี สำหรับ PHP ขอแนะนำZend\Crypt
แพ็คเกจของ Zend Framework 2 ที่จริงแล้วเป็นสิ่งเดียวที่ฉันแนะนำ ณ จุดปัจจุบันนี้ ได้รับการตรวจสอบอย่างดีเยี่ยมและทำการตัดสินใจทั้งหมดสำหรับคุณ (ซึ่งเป็นสิ่งที่ดีมาก) ...
สิ่งที่ต้องการ:
use Zend\Crypt\BlockCipher;
public function createHash($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
return $blockCipher->encrypt($hash);
}
public function verifyHash($password, $hash) {
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
$hash = $blockCipher->decrypt($hash);
return password_verify($password, $hash);
}
และเป็นประโยชน์เพราะคุณใช้อัลกอริทึมทั้งหมดในรูปแบบที่เข้าใจดีและศึกษามาอย่างดี (อย่างน้อยที่สุด) จำไว้ว่า:
ไม่ว่าใครก็ตามตั้งแต่มือสมัครเล่นที่ไร้เดียงสาไปจนถึงนักเข้ารหัสที่เก่งที่สุดสามารถสร้างอัลกอริทึมที่ตัวเขาเองไม่สามารถทำลายได้