คุณสามารถค้นหาในWikipedia ... แต่เนื่องจากคุณต้องการคำอธิบายฉันจะทำให้ดีที่สุดที่นี่:
ฟังก์ชันแฮช
จัดเตรียมการแม็พระหว่างอินพุตความยาวตามอำเภอใจและเอาต์พุต (ปกติ) ความยาวคงที่ (หรือความยาวน้อยกว่า) มันสามารถเป็นอะไรก็ได้ตั้งแต่ crc32 ธรรมดาไปจนถึงฟังก์ชั่นแฮชเข้ารหัสเต็มรูปแบบเช่น MD5 หรือ SHA1 / 2/256/512 ประเด็นคือมีการทำแผนที่ทางเดียวที่เกิดขึ้น มีการแมปจำนวนมากเสมอ: 1 (หมายถึงจะมีการชนกันอยู่เสมอ) เนื่องจากทุกฟังก์ชั่นให้ผลผลิตที่น้อยกว่าความสามารถในการป้อนข้อมูล (หากคุณป้อนไฟล์ 1mb ที่เป็นไปได้ทั้งหมดลงใน MD5 คุณจะได้รับการชนจำนวนมาก)
เหตุผลที่พวกเขายาก (หรือเป็นไปไม่ได้ในการปฏิบัติจริง) เพื่อย้อนกลับเป็นเพราะพวกเขาทำงานภายใน ฟังก์ชันแฮชการเข้ารหัสลับส่วนใหญ่วนซ้ำไปตามอินพุตที่ตั้งไว้หลายครั้งเพื่อสร้างเอาต์พุต ดังนั้นถ้าเราดูแต่ละอันของความยาวคงที่ (ซึ่งขึ้นอยู่กับอัลกอริทึม) ฟังก์ชันแฮชจะเรียกสถานะปัจจุบันว่า จากนั้นจะวนซ้ำสถานะและเปลี่ยนเป็นสถานะใหม่และใช้เป็นข้อเสนอแนะในตัวเอง (MD5 ทำเช่นนี้ 64 ครั้งสำหรับแต่ละบิตข้อมูล 512 บิต) จากนั้นจะรวมสถานะผลลัพธ์จากการวนซ้ำทั้งหมดเหล่านี้กลับเข้าด้วยกันเพื่อสร้างแฮชผลลัพธ์
ตอนนี้ถ้าคุณต้องการถอดรหัสแฮชก่อนอื่นคุณต้องหาวิธีแยกแฮชที่ให้ออกไปเป็นสถานะที่มีการวนซ้ำ (1 ความเป็นไปได้สำหรับอินพุตที่เล็กกว่าขนาดของก้อนข้อมูล จากนั้นคุณจะต้องย้อนกลับการวนซ้ำสำหรับแต่ละรัฐ ตอนนี้จะอธิบายว่าทำไมนี้เป็นเรื่องยากมากคิดพยายามที่จะอนุมานa
และจากสูตรดังต่อไปนี้:b
10 = a + b
มีชุดค่าผสม 10 ค่าบวกa
และb
สามารถใช้งานได้ ทีนี้วนซ้ำหลาย ๆ ครั้ง:tmp = a + b; a = b; b = tmp
. สำหรับการทำซ้ำ 64 ครั้งคุณจะมีความเป็นไปได้มากกว่า 10 ^ 64 ที่จะลอง และนั่นเป็นเพียงการเพิ่มที่เรียบง่ายซึ่งบางรัฐถูกสงวนไว้จากการทำซ้ำไปยังการทำซ้ำ ฟังก์ชันแฮชจริงทำมากกว่า 1 การดำเนินการ (MD5 ทำงานได้ประมาณ 15 การดำเนินการกับตัวแปรสถานะ 4 ตัว) และเนื่องจากการวนซ้ำครั้งต่อไปขึ้นอยู่กับสถานะของก่อนหน้านี้และก่อนหน้านี้ถูกทำลายในการสร้างสถานะปัจจุบันมันเป็นไปไม่ได้เลยที่จะกำหนดสถานะอินพุตที่นำไปสู่สถานะเอาต์พุตที่กำหนด (สำหรับแต่ละการวนซ้ำไม่น้อย) รวมกับความเป็นไปได้จำนวนมากที่เกี่ยวข้องและการถอดรหัสแม้ MD5 จะใช้ทรัพยากรจำนวนใกล้เคียง (แต่ไม่ จำกัด ไม่) ทรัพยากรมากมายที่มัน '
ฟังก์ชั่นการเข้ารหัส
พวกเขาจัดทำแผนที่ 1: 1 ระหว่างอินพุตและเอาต์พุตความยาวโดยพลการ และพวกเขากลับได้เสมอ สิ่งสำคัญที่ควรทราบคือมันสามารถย้อนกลับได้โดยใช้วิธีการบางอย่าง และเป็น 1: 1 สำหรับคีย์ที่กำหนด ตอนนี้มีหลายอินพุต: คู่คีย์ที่อาจสร้างเอาต์พุตเดียวกัน (อันที่จริงมักจะมีขึ้นอยู่กับฟังก์ชันการเข้ารหัส) ข้อมูลที่เข้ารหัสที่ดีนั้นแยกไม่ออกจากสัญญาณรบกวนแบบสุ่ม ซึ่งแตกต่างจากเอาต์พุตแฮชที่ดีซึ่งอยู่ในรูปแบบที่สอดคล้องกันเสมอ
ใช้เคส
ใช้ฟังก์ชันแฮชเมื่อคุณต้องการเปรียบเทียบค่า แต่ไม่สามารถเก็บการแทนค่าธรรมดา (ด้วยเหตุผลใด ๆ ก็ได้) รหัสผ่านควรเหมาะกับกรณีการใช้งานนี้เป็นอย่างดีเนื่องจากคุณไม่ต้องการเก็บข้อความธรรมดาไว้ด้วยเหตุผลด้านความปลอดภัย (และไม่ควร) แต่ถ้าคุณต้องการตรวจสอบระบบไฟล์สำหรับไฟล์เพลงที่ละเมิดลิขสิทธิ์ล่ะ จะไม่สามารถจัดเก็บไฟล์ 3 MB ต่อไฟล์เพลง ดังนั้นให้ใช้แฮชของไฟล์แทนและเก็บไว้ (md5 จะเก็บ 16 ไบต์แทน 3mb) ด้วยวิธีนี้คุณเพียงแค่แฮชไฟล์แต่ละไฟล์และเปรียบเทียบกับฐานข้อมูลแฮชที่เก็บไว้ (วิธีนี้ไม่ได้ผลเช่นกันเนื่องจากการเข้ารหัสใหม่การเปลี่ยนส่วนหัวของไฟล์ ฯลฯ แต่เป็นตัวอย่างการใช้งาน)
ใช้ฟังก์ชันแฮชเมื่อคุณกำลังตรวจสอบความถูกต้องของข้อมูลอินพุต นั่นคือสิ่งที่พวกเขาออกแบบมา หากคุณมีอินพุต 2 ชิ้นและต้องการตรวจสอบว่าเหมือนกันหรือไม่ให้รันทั้งสองผ่านฟังก์ชั่นแฮช ความน่าจะเป็นของการชนนั้นต่ำมากสำหรับขนาดอินพุตที่เล็ก (สมมติว่าเป็นฟังก์ชันแฮชที่ดี) นี่คือเหตุผลที่แนะนำให้ใช้รหัสผ่าน สำหรับรหัสผ่านสูงสุด 32 ตัวอักษร md5 มีพื้นที่เอาต์พุต 4 เท่า SHA1 มีพื้นที่เอาต์พุต 6 เท่า (โดยประมาณ) SHA512 มีพื้นที่เอาต์พุตประมาณ 16 เท่า คุณไม่สนใจจริงๆว่ารหัสผ่านคืออะไรคุณสนใจหรือไม่ว่าเป็นรหัสเดียวกับที่จัดเก็บไว้ นั่นเป็นเหตุผลที่คุณควรใช้แฮชสำหรับรหัสผ่าน
ใช้การเข้ารหัสทุกครั้งที่คุณต้องการดึงข้อมูลอินพุตออกมา ขอให้สังเกตคำว่าจำเป็น หากคุณกำลังเก็บหมายเลขบัตรเครดิตคุณจำเป็นต้องนำหมายเลขเหล่านั้นออกมาในบางจุด แต่ไม่ต้องการจัดเก็บข้อความธรรมดา ดังนั้นให้เก็บรุ่นที่เข้ารหัสไว้และรักษารหัสให้ปลอดภัยที่สุดเท่าที่จะทำได้
ฟังก์ชั่นแฮชยังยอดเยี่ยมสำหรับการลงนามข้อมูล ตัวอย่างเช่นหากคุณกำลังใช้ HMAC คุณจะต้องเซ็นชื่อชิ้นส่วนของข้อมูลโดยทำการแฮชของข้อมูลที่เชื่อมโยงกับค่าที่ทราบ แต่ไม่ได้ส่ง (ค่าลับ) ดังนั้นคุณส่งข้อความธรรมดาและแฮช HMAC จากนั้นผู้รับเพียงแค่แฮชข้อมูลที่ส่งด้วยค่าที่รู้จักและตรวจสอบเพื่อดูว่ามันตรงกับ HMAC ส่ง หากเป็นเช่นนั้นคุณจะรู้ว่าบุคคลนั้นไม่ได้ถูกดัดแปลงโดยไม่มีค่าความลับ โดยทั่วไปจะใช้ในระบบคุกกี้ที่ปลอดภัยโดยเฟรมเวิร์ก HTTP รวมถึงในการส่งข้อความของข้อมูลผ่าน HTTP ที่คุณต้องการความมั่นใจในความสมบูรณ์ของข้อมูล
หมายเหตุเกี่ยวกับแฮชสำหรับรหัสผ่าน:
คุณลักษณะที่สำคัญของฟังก์ชั่นแฮชการเข้ารหัสคือพวกมันควรจะสร้างได้เร็วและยากมาก / ช้าที่จะย้อนกลับ สิ่งนี้ทำให้เกิดปัญหากับรหัสผ่าน หากคุณจัดเก็บsha512(password)
คุณจะไม่ทำสิ่งใดเพื่อป้องกันตารางรุ้งหรือการโจมตีแบบดุร้าย จำไว้ว่าฟังก์ชั่นแฮชถูกออกแบบมาเพื่อความเร็ว ดังนั้นจึงเป็นเรื่องเล็กน้อยที่ผู้โจมตีจะเรียกใช้พจนานุกรมผ่านฟังก์ชั่นแฮชและทดสอบผลลัพธ์แต่ละรายการ
การเพิ่มเกลือช่วยเรื่องสำคัญเพราะมันจะเพิ่มข้อมูลที่ไม่รู้จักเล็กน้อยลงในแฮช ดังนั้นแทนที่จะค้นหาสิ่งที่ตรงกันmd5(foo)
พวกเขาต้องค้นหาสิ่งที่เมื่อเพิ่มเข้าไปในเกลือที่รู้จักก่อให้เกิดmd5(foo.salt)
(ซึ่งยากมากที่จะทำ) แต่ก็ยังไม่สามารถแก้ปัญหาความเร็วได้เพราะถ้าพวกเขารู้ว่าเกลือมันเป็นเพียงเรื่องของการเรียกใช้พจนานุกรมผ่าน
ดังนั้นจึงมีวิธีจัดการกับสิ่งนี้ วิธีหนึ่งที่ได้รับความนิยมเรียกว่าการเพิ่มความแข็งแกร่งของคีย์ (หรือการยืดคีย์) โดยพื้นฐานแล้วคุณจะวนซ้ำหลายครั้ง มันทำสองสิ่ง ครั้งแรกมันช้าลงรันไทม์ของอัลกอริทึมการแปลงแป้นพิมพ์อย่างมีนัยสำคัญ ประการที่สองหากดำเนินการถูกต้อง (ผ่านอินพุตและเกลือกลับเข้าไปในแต่ละการวนซ้ำ) จริง ๆ แล้วจะเพิ่มเอนโทรปี (พื้นที่ว่าง) สำหรับเอาท์พุทลดโอกาสของการชน การดำเนินการเล็กน้อยคือ:
var hash = password + salt;
for (var i = 0; i < 5000; i++) {
hash = sha512(hash + password + salt);
}
อื่น ๆ ที่มีการใช้งานมาตรฐานมากขึ้นเช่นPBKDF2 , bcrypt แต่เทคนิคนี้ใช้โดยระบบที่เกี่ยวข้องกับความปลอดภัยค่อนข้างน้อย (เช่น PGP, WPA, Apache และ OpenSSL)
บรรทัดล่างhash(password)
ไม่ดีพอ hash(password + salt)
ดีกว่า แต่ก็ยังไม่ดีพอ ... ใช้กลไกแฮชที่ยืดออกเพื่อสร้างรหัสผ่านแฮชของคุณ ...
หมายเหตุเกี่ยวกับการยืดเล็กน้อย
ไม่ว่าในกรณีใด ๆ ให้ป้อนเอาต์พุตของแฮ็ชหนึ่งอันกลับไปยังฟังก์ชันแฮชโดยตรง :
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash); // <-- Do NOT do this!
}
เหตุผลนี้เกี่ยวข้องกับการชน โปรดจำไว้ว่าฟังก์ชันแฮชทั้งหมดมีการชนกันเนื่องจากพื้นที่เอาต์พุตที่เป็นไปได้ (จำนวนเอาต์พุตที่เป็นไปได้) มีขนาดเล็กกว่าพื้นที่อินพุตนั้น เพื่อดูว่าทำไมเรามาดูสิ่งที่เกิดขึ้น เพื่อบทนี้ขอให้สมมติฐานที่ว่ามีโอกาสที่ 0.001% ของการชนจากsha1()
(มันมากต่ำกว่าในความเป็นจริง แต่สำหรับวัตถุประสงค์ในการสาธิต)
hash1 = sha1(password + salt);
ตอนนี้hash1
มีความน่าจะเป็นของการชนที่ 0.001% แต่เมื่อเราทำต่อไปhash2 = sha1(hash1);
, การชนกันทั้งหมดของhash1
hash2
ชนกลายเป็นโดยอัตโนมัติ ดังนั้นตอนนี้เรามีอัตราแฮช 1 ที่ 0.001% และการsha1()
โทรครั้งที่สองเพิ่มเข้าไป ดังนั้นตอนนี้hash2
มีความน่าจะเป็นของการชนกันของ 0.002% นั่นเป็นโอกาสที่สองเท่า! การวนซ้ำแต่ละครั้งจะเพิ่ม0.001%
โอกาสการชนอีกครั้งกับผลลัพธ์ ดังนั้นด้วยการวนซ้ำ 1,000 ครั้งโอกาสในการปะทะกันเพิ่มขึ้นจาก 0.001% เป็น 1% ตอนนี้การย่อยสลายเป็นเส้นตรงและความน่าจะเป็นจริงไกลขนาดเล็ก แต่ผลที่ได้คือเดียวกัน (การประเมินของโอกาสของการปะทะกันครั้งเดียวที่มีmd5
ประมาณ 1 / (2 128 ) หรือ 1 / (3x10 38) แม้ว่าจะดูเล็ก แต่ต้องขอบคุณการโจมตีวันเกิดแต่ก็ไม่เล็กเท่าที่ควร)
แต่โดยการเพิ่มเกลือและรหัสผ่านใหม่ทุกครั้งคุณจะแนะนำข้อมูลกลับไปที่ฟังก์ชันแฮช ดังนั้นการชนใด ๆ ในรอบใดก็ไม่ใช่การชนในรอบถัดไปอีกต่อไป ดังนั้น:
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash + password + salt);
}
มีโอกาสชนกันกับsha512
ฟังก์ชันเนทิฟ สิ่งที่คุณต้องการ ใช้สิ่งนั้นแทน