ทางเลือกในการส่งรหัสผ่านข้อความธรรมดาขณะเข้าสู่ระบบ

Jan 02 2021

หมายเหตุ: ฉันได้อ่านแล้วสามารถส่งรหัสผ่านข้อความธรรมดาผ่าน HTTPS ได้หรือไม่? และความปลอดภัย https - ควรแฮชรหัสผ่านฝั่งเซิร์ฟเวอร์หรือฝั่งไคลเอ็นต์? แต่นี่เป็นวิธีการเปลี่ยนเฉพาะ (ดูด้านล่าง)


หลังจากอ่านบทความเกี่ยวกับวิธีการตรวจสอบสิทธิ์แบบใหม่ในบล็อก Cloudflareฉันได้ตรวจสอบPOSTคำขอที่ส่งไปขณะตรวจสอบสิทธิ์ด้วย "เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์> เครือข่าย" เว็บไซต์ยอดนิยมหลายแห่ง (Reddit, HN และอื่น ๆ ) ยังคงส่งรหัสผ่านเป็นข้อความธรรมดาในPOSTคำขอ ( SSL-secure) (ดูภาพหน้าจอด้านล่าง)

วิธีการเข้าสู่ระบบนี้ยังคงเป็นมาตรฐานอุตสาหกรรมหรือไม่?

ทางเลือกต่อไปนี้ปลอดภัยกว่าการส่งรหัสผ่านข้อความธรรมดาผ่าน HTTPS หรือไม่?

  • การสมัคร: ไคลเอนต์สร้างแบบสุ่มsaltและส่งทูเพิล(username, salt, hash(plain_password + salt))ผ่านการPOSTร้องขอ จากนั้นรหัสผ่านข้อความธรรมดาจะไม่ไปถึงเซิร์ฟเวอร์

  • การเข้าสู่ระบบที่ตามมา: เซิร์ฟเวอร์ต้องส่งsaltกลับไปยังไคลเอนต์ใด ๆที่พยายามเข้าสู่ระบบด้วยที่กำหนดusernameเพื่อให้ไคลเอนต์สามารถแฮชด้วยเกลือเดียวกันได้ ดังนั้นหมายความว่าเซิร์ฟเวอร์กำลังเปิดเผยsaltต่อทุกคนที่พยายามเข้าสู่ระบบด้วยชื่อผู้ใช้ที่กำหนด

  • ประโยชน์: เซิร์ฟเวอร์เก็บรหัสผ่านที่ถูกแฮช + เค็ม (ซึ่งเป็นมาตรฐาน) แต่เซิร์ฟเวอร์ยังไม่เคยเห็นรหัสผ่านข้อความธรรมดาเลยแม้แต่ครั้งเดียว (ดังนั้นหากเซิร์ฟเวอร์ถูกบุกรุกความเสี่ยงจะถูก จำกัด )

หมายเหตุ:

  • ตั้งแต่H = hash(plain_password + salt)ตอนนี้ทำงานนิด ๆ หน่อย ๆ เหมือนใหม่plaintext(ดูคำตอบที่ 2 ของศูนย์ความรู้หลักฐานรหัสผ่าน: ทำไม hashing รหัสผ่านบนฝั่งไคลเอ็นต์ที่ไม่ได้เป็น ZKP ) แล้วตัดสามารถจัดเก็บในฐานข้อมูลแทน(username, salt, server_salt, hash(H + server_salt))(username, salt, H)

  • เพื่อลดความเสี่ยงสำหรับการโจมตีซ้ำเซิร์ฟเวอร์ยังสามารถส่งข้อมูลที่ไม่ซ้ำกันnonceพร้อมกับsaltการเข้าสู่ระบบแต่ละครั้งที่หมดอายุหลังจากพยายามเข้าสู่ระบบหนึ่งครั้ง

  • เป้าหมายหลักที่นี่คือเซิร์ฟเวอร์ไม่เคยเข้าถึงรหัสผ่านข้อความธรรมดาหรือแฮชง่ายๆเลย (ซึ่งมักจะย้อนกลับด้วยตารางรุ้งเดียวสำหรับทั้งไซต์) ฉันตกลงกับความเสี่ยงที่ผู้โจมตีมีการคำนวณตารางรุ้งหนึ่งต่อผู้ใช้

  • ตัวอย่างการโจมตีที่ฉันต้องการลด: หากเซิร์ฟเวอร์สามารถเข้าถึงรหัสผ่านข้อความธรรมดาและถูกบุกรุก (ตัวอย่างเช่น Spectre / Meltdown vuln.) รหัสผ่านข้อความธรรมดาของผู้ใช้ (อาจใช้ซ้ำในเว็บไซต์อื่น) อาจถูกขโมยก่อนที่จะถูกเค็ม - แฮชและบันทึกลงในฐานข้อมูล


คำตอบ

14 SteffenUllrich Jan 02 2021 at 14:38

ฉันไม่เห็นว่าข้อเสนอของคุณดีไปกว่าแนวทางการแฮชฝั่งไคลเอ็นต์ที่มีอยู่ แต่ฉันพบว่ามีความซับซ้อนในการนำไปใช้มากกว่าข้ออื่น ๆ น่าเสียดายที่คุณไม่ได้อธิบายถึงความเสี่ยงเฉพาะที่คุณพยายามเข้าถึงดังนั้นฉันจึงถือว่าภัยคุกคามทั่วไปที่พบเห็นได้ทั่วไป

Man in the Middle Attacker

ในกรณีนี้มันจะสันนิษฐานได้ว่าบางคนที่อยู่ตรงกลางที่มีการเข้าถึงการจราจรเช่นเพราะมันถูกบุกรุกบางสกัดกั้นการจราจร TLS ที่เชื่อถือได้ในไฟร์วอลล์ขององค์กรหรือได้ถือใน CA ที่เชื่อถือเช่นในกรณีของSuperfish

ในสถานการณ์สมมตินี้โจมตีได้รับการเข้าถึงเช่นเดียวกับที่พวกเขาทำมาก่อนด้วยH plain_passwordตั้งแต่Hเป็นทุกสิ่งที่จำเป็นสำหรับการตรวจสอบผู้บุกรุกจะประสบความสำเร็จจึงและวิธีการของคุณไม่ได้เพิ่มการป้องกันใด ๆ เพิ่มเติมที่นี่

การซ่อนรหัสผ่านที่ไม่รัดกุมและการใช้รหัสผ่านซ้ำ

อาร์กิวเมนต์ทั่วไปสำหรับการแฮชฝั่งไคลเอ็นต์คือการไม่เปิดเผยรหัสผ่านที่อ่อนแอหรือใช้ซ้ำกับเซิร์ฟเวอร์ แต่ให้พิสูจน์ตัวตนด้วยรหัสผ่านที่ได้รับที่ซับซ้อนแทน แนวทางของคุณทำเช่นนี้ด้วยการแฮชplain_passwordกับผู้ใช้บางรายที่สร้างแบบสุ่มsaltจากนั้นส่งHและsaltไปยังเซิร์ฟเวอร์ในการตั้งค่ารหัสผ่าน

แม้ว่าจะใช้งานได้ทุกการรับรองความถูกต้องในขณะนี้ต้องมีขั้นตอนเพิ่มเติม : ก่อนอื่นต้องดึงเกลือที่ใช้ก่อนหน้านี้สำหรับผู้ใช้จากผู้ใช้จากนั้นจึงสามารถใช้สิ่งนี้saltเพื่อแฮชไฟล์plain_password. ขั้นตอนเพิ่มเติมนี้ทำให้การพิสูจน์ตัวตนซับซ้อนขึ้นตั้งแต่แรกต้องตรวจสอบผู้ใช้กับเซิร์ฟเวอร์จากนั้นจึงตรวจสอบรหัสผ่านได้ นอกจากนี้การใช้งานที่ไม่สำคัญนี้จะเป็นการเปิดการรั่วไหลของข้อมูลเนื่องจากทำให้สามารถตรวจสอบได้ว่ามีผู้ใช้อยู่ตั้งแต่แรก (เกลือที่ส่งคืนหรือไม่) โดยไม่ต้องมีการตรวจสอบสิทธิ์เพิ่มเติม

การรั่วไหลของข้อมูลนี้สามารถปิดได้โดยเซิร์ฟเวอร์ส่งคืนเกลือบางส่วนไม่ว่าผู้ใช้จะมีอยู่หรือไม่ก็ตาม แน่นอนว่านี่ไม่สามารถเป็นเพียงเกลือแบบสุ่มได้เนื่องจากมิฉะนั้นผู้โจมตีสามารถตรวจสอบผู้ใช้คนเดียวกันได้สองครั้งและสรุปได้ว่าไม่มีผู้ใช้หากเกลือที่ส่งคืนแตกต่างกัน ดังนั้นเกลือจึงต้องได้รับการแก้ไขสำหรับผู้ใช้ที่ไม่มีอยู่นั่นคือได้มาจากชื่อผู้ใช้

และนี้ยังแสดงให้เห็นถึงเส้นทางไปสู่การลดความซับซ้อนของวิธีการของคุณ : แทนการสร้างเกลือสุ่มโดยผู้ใช้เก็บไว้ที่เซิร์ฟเวอร์และเรียกในภายหลังอีกครั้งหนึ่งก็อาจจะเป็นผลมาจากเกลือชื่อผู้ใช้ที่ฝั่งไคลเอ็นต์ ง่ายsalt=hash(username+domain)จะเพียงพอที่จะสร้างเกลือซึ่งเป็นเอกลักษณ์ต่อโดเมนและทำให้ทั้งสองsaltและHที่แตกต่างกันแม้ว่าusernameและplain_passwordได้นำกลับมาใช้ในโดเมนที่แตกต่างกัน และตรงกันข้ามกับแนวทางของคุณไม่จำเป็นต้องมีการเดินทางไปยังเซิร์ฟเวอร์เพิ่มเติมเพื่อดึงเกลือที่ใช้ก่อนหน้านี้ให้กับผู้ใช้ก่อน


กล่าวโดยย่อ: วิธีการที่เรียบง่ายนี้โดยทั่วไปจะส่งhash(plain_password+username+domain)แทนรหัสผ่านเดิม โดเมนจะถูกเพิ่มเพื่อให้แน่ใจว่าแม้ว่าusernameและplain_passwordจะนำกลับมาใช้ในโรงงานต่างๆรหัสผ่านมาที่ไม่ได้นำมาใช้ใหม่

8 mti2935 Jan 02 2021 at 21:33

นี่คือปัญหาที่โปรโตคอลเช่น PAKE และSRPมุ่งมั่นที่จะแก้ไข ด้วย PAKE / SRP ไคลเอนต์และเซิร์ฟเวอร์จะพิสูจน์ตัวตนซึ่งกันและกันตามรหัสผ่านที่ไคลเอ็นต์รู้จัก (และรหัสผ่านที่เซิร์ฟเวอร์รู้จัก)

ไคลเอ็นต์สาธิตให้เซิร์ฟเวอร์ทราบรหัสผ่านโดยที่ไคลเอ็นต์ไม่ส่งรหัสผ่าน (หรือข้อมูลที่เทียบเท่ารหัสผ่าน) ไปยังเซิร์ฟเวอร์ ในตอนท้ายของกระบวนการไคลเอนต์และเซิร์ฟเวอร์แชร์ความลับร่วมกัน

เซิร์ฟเวอร์ไม่เก็บรหัสผ่าน (หรือข้อมูลที่เทียบเท่ารหัสผ่าน) และไม่เสี่ยงต่อการโจมตีจากพจนานุกรม ผู้ดักฟังหรือ man-in-the-middle ที่สามารถดูข้อความธรรมดาที่ส่งผ่านสายจะไม่สามารถรับข้อมูลเพียงพอที่จะได้รับรหัสผ่าน วิธีนี้ช่วยป้องกันการโจมตีจากคนตรงกลางโดยใช้ใบรับรองปลอมได้อย่างมีประสิทธิภาพและป้องกันไม่ให้ไซต์ 'ฟิชชิง' ขโมยรหัสผ่านของผู้ใช้

หากต้องการทราบรายละเอียดที่ดีเกี่ยวกับวิธีการใช้ 1 รหัสผ่าน SRP โปรดดู https://blog.1password.com/developers-how-we-use-srp-and-you-can-too/

5 mentallurg Jan 02 2021 at 16:00

นอกจากคำตอบของ Steffen Ullrich :

หากในระหว่างการเข้าสู่ระบบผู้ใช้ส่งเฉพาะแฮชผู้โจมตีไม่จำเป็นต้องรู้รหัสผ่าน มันเพียงพอที่จะขโมยฐานข้อมูลรหัสผ่าน จากนั้นในระหว่างการร้องขอการเข้าสู่ระบบผู้โจมตีจะส่งแฮชจากฐานข้อมูล เซิร์ฟเวอร์จะไม่แยกความแตกต่างว่าไคลเอนต์ใช้รหัสผ่านและแฮชหรือหากไคลเอนต์ (ผู้โจมตี) เพียงแค่ส่งแฮช

บทความเกี่ยวกับที่อยู่ OPAQUE ก็มีปัญหาเช่นกัน: การขโมยฐานข้อมูลรหัสผ่านจะไม่ช่วยผู้โจมตี หนึ่งจะต้องรู้รหัสผ่านผู้ใช้ธรรมดา

3 MargaretBloom Jan 03 2021 at 08:43

หากผู้โจมตีโจมตีเซิร์ฟเวอร์ของคุณพวกเขาอยู่ในการควบคุมไม่เพียง แต่ซอฟต์แวร์ที่ทำงานบนเซิร์ฟเวอร์ของคุณเท่านั้น แต่ยังรวมถึงซอฟต์แวร์ที่ทำงานบนไคลเอนต์ด้วย
ไม่ว่าคุณจะออกแบบรูปแบบการรับรองความถูกต้องที่ออกแบบมาอย่างสวยงามผู้โจมตีสามารถแก้ไขได้ก่อนที่จะส่งไปยังเบราว์เซอร์
ตอนนี้คุณมีปัญหาไก่ไข่: คุณไม่สามารถรักษารหัสผ่านให้ปลอดภัยได้หากผู้โจมตีควบคุมวิธีรวบรวมและส่งไปยังเซิร์ฟเวอร์ของคุณ

หากคุณกังวลเกี่ยวกับการละเมิดข้อมูลวิธีการของคุณจะใช้เป็นการป้องกัน แต่รหัสผ่านที่เหมาะสมก็จะแฮชฝั่งเซิร์ฟเวอร์เช่นกัน

หากคุณกังวลเกี่ยวกับการโจมตีของ MITM TLS จะแก้ไขได้
หากคุณกังวลเกี่ยวกับการโจมตี MITM บน TLS อย่างที่ฉันอยากจะบอกว่าการป้องกันที่ดีต่อพวกเขามักจะเริ่มต้นด้วยคู่มือ Krav Maga ผู้โจมตีที่มีทรัพยากรเพียงพอที่จะทำลาย TLS ได้อย่างต่อเนื่องไม่มีปัญหาในการได้รับสิ่งที่ต้องการจากบุคคลที่ไม่ได้รับการฝึกฝนอย่างถูกต้องและได้รับการฝึกฝนมาเป็นพิเศษ (ใช่ฉันกำลังพูดถึงการทรมานการแบล็กเมล์การลักพาตัวและการฆาตกรรม)

หากคุณกังวลเกี่ยวกับผู้คุกคามที่สามารถอ่านได้เฉพาะข้อมูลที่เซิร์ฟเวอร์ได้รับมาแนวทางของคุณ (ตามที่ Steffen แก้ไขแล้ว) จะต่อต้านพวกเขา อย่างไรก็ตามนี่เป็นสถานการณ์ที่แปลกและหายากซึ่งมักเกิดจากเซิร์ฟเวอร์ที่กำหนดค่าผิดอย่างร้ายแรงและแนวทางปฏิบัติที่ไม่ดีในการพัฒนา (เช่นการส่งข้อมูลรับรองผ่านคำขอ GET และการจัดเก็บบันทึกการเข้าถึงแบบสาธารณะ) การแก้ไขข้อผิดพลาดเหล่านี้ง่ายกว่าการคิดค้นโปรโตคอลเพียงเพื่อจัดการกับข้อผิดพลาดเหล่านี้

โปรดทราบว่าช่องโหว่ทั้งสองที่คุณกล่าวถึง (จริงๆแล้วเป็นเพียงช่องโหว่เดียวเนื่องจาก Meltdown เป็นตัวแปรทางเทคนิคของ Spectre) ในที่สุดจะส่งผลให้มีการเพิ่มสิทธิ์ในพื้นที่ทำให้ผู้โจมตีสามารถควบคุมเว็บเซิร์ฟเวอร์ของคุณได้อย่างเต็มที่ เน้นย้ำอีกครั้งว่าสถานการณ์ที่ผู้โจมตีเข้าถึงข้อมูลที่ได้รับจากเว็บเซิร์ฟเวอร์ของคุณนั้นหายากเพียงใด

ดังนั้นเหตุผลที่ไซต์ใหญ่ ๆ หลายแห่งไม่ใช้มันเป็นเพราะมันไม่ได้เพิ่มอะไรเลยนอกจากในสถานการณ์เฉพาะที่มักจะมีการกำหนดค่าผิดพลาด นอกจากนี้ยังเป็นที่น่าสังเกตว่าหากผู้โจมตีสามารถอ่านข้อมูลที่กำลังเปลี่ยนไปที่เซิร์ฟเวอร์ของคุณได้แสดงว่าคุณเป็นฝ่ายแพ้ของเกม โปรดทราบว่าเป็นเรื่องดีที่จะมีการป้องกันแบบหลายชั้น แต่เป้าหมายหลักของคุณไม่ใช่ให้เกิดขึ้นตั้งแต่แรก และการมุ่งเน้นไปที่สิ่งนั้นจะช่วยให้คุณไม่ต้องคิดค้นแผนการใหม่ ๆ

อย่างไรก็ตามตามที่ Steffen แสดงให้เห็นว่าแผนการที่คุณเสนออาจใช้รูปแบบการโจมตีแปลก ๆ ได้อีก ฉันจะยังคงใช้hash(hash(domain + username) + password)แทนที่จะใช้hash(domain + username + password)เพื่อควบคุมความเป็นไปได้ระยะไกลที่domain + username + passwordยังคงเป็นคำในพจนานุกรม
ดังที่ mti2935 แสดงให้เห็นว่า SRP เป็นทางเลือกที่น่าสนใจกว่า การรับรองความถูกต้องโดยใช้ใบรับรอง (เช่นการจัดการโดยเบราว์เซอร์) เป็นอีกทางเลือกหนึ่ง (ซึ่งฉันพบว่าดีกว่าการทำด้วยตนเองในสคริปต์ JS ที่อาจปนเปื้อนซึ่งอาจเป็นอันตรายตามที่คุณเสนอไว้ในความคิดเห็น)