ฉันควรใช้ประเภท / ความยาวคอลัมน์ใดในการจัดเก็บรหัสผ่านที่แฮช Bcrypt ในฐานข้อมูล


318

ฉันต้องการเก็บรหัสผ่านที่แฮช (โดยใช้ BCrypt) ในฐานข้อมูล อะไรคือสิ่งที่ดีสำหรับสิ่งนี้และความยาวที่ถูกต้องคืออะไร? รหัสผ่านถูกแฮชด้วย BCrypt ยาวเท่ากันหรือไม่

แก้ไข

ตัวอย่างแฮช:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

หลังจากแฮชรหัสผ่านบางส่วนดูเหมือนว่า BCrypt จะสร้างแฮช 60 ตัวเสมอ

แก้ไข 2

ขออภัยที่ไม่ได้กล่าวถึงการใช้งาน ฉันใช้jBCrypt


ดูที่กรอบการแฮชรหัสผ่าน PHPของ Openwall (PHPass) แบบพกพาและแข็งต่อการโจมตีทั่วไปจำนวนมากในรหัสผ่านผู้ใช้ คนที่เขียนกรอบ (SolarDesigner) เป็นคนเดียวกับที่เขียนจอห์นเดอะริปเปอร์และนั่งเป็นผู้พิพากษาในรหัสผ่าน Hashing การแข่งขัน เขารู้สิ่งหนึ่งหรือสองเกี่ยวกับการโจมตีรหัสผ่าน
jww

1
หากใครตรงนี้กำลังมองหาวิธีการแก้ปัญหาสำหรับการสแกน : คำตอบของ Gumbo ยังใช้กับการสแกน ฉันใช้ BINARY (64) ใน MySQL เป็นการส่วนตัวและอนุญาตให้ฉันทดสอบความเท่าเทียมกันของไบต์ภายใต้ Python ในภายหลัง
Philippe Hebert

คำตอบ:


369

รูปแบบ crypt แบบแยกส่วนสำหรับ bcrypt ประกอบด้วย

  • $2$, $2a$หรือ$2y$การระบุขั้นตอนวิธีการและรูปแบบคร่ำเครียด
  • ค่าตัวเลขสองหลักหมายถึงพารามิเตอร์ต้นทุนตามด้วย $
  • 53 ตัวอักษรค่ายาวฐาน 64 เข้ารหัส (พวกเขาใช้ตัวอักษร., /, 0- 9, A- Z, a- zที่แตกต่างกับฐานมาตรฐานการเข้ารหัส 64ตัวอักษร) ประกอบด้วย:
    • เกลือ 22 ตัว (มีประสิทธิภาพเพียง 128 บิตจาก 132 บิตที่ถอดรหัส)
    • 31 อักขระของเอาต์พุตที่เข้ารหัส (มีประสิทธิภาพเพียง 184 บิตของ 186 บิตที่ถอดรหัส)

ดังนั้นความยาวทั้งหมดคือ 59 หรือ 60 ไบต์ตามลำดับ

เมื่อคุณใช้รูปแบบ 2a คุณจะต้องมี 60 ไบต์ ดังนั้นสำหรับ MySQL ฉันจะแนะนำให้ใช้CHAR(60) BINARYหรือBINARY(60) (ดูThe _binและการเปรียบเทียบไบนารีสำหรับข้อมูลเกี่ยวกับความแตกต่าง)

CHARไม่ปลอดภัยแบบไบนารีและความเท่าเทียมกันไม่ได้ขึ้นอยู่กับค่าไบต์เท่านั้น แต่ขึ้นอยู่กับการเปรียบเทียบจริง ในกรณีที่เลวร้ายที่สุดได้รับการปฏิบัติเป็นไปได้เท่ากันA aดูThe _binand binaryCollationสำหรับข้อมูลเพิ่มเติม


28
ระวัง - การจัดเก็บเป็นไบนารี (60) อาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดสำหรับความเท่าเทียมกันของสตริง (เหนือสิ่งอื่นใด) ใน. NET นี้สามารถเอาชนะได้โดยใช้ String.Equals (จาก DataBaseBinary60string, commonishString, StringComparison.InvariantCulture)
JHubbard80

8
หากคุณกำหนดคอลัมน์เป็น CHAR (60) CHARACTER SET latin1 COLLATE latin1_bin ตอนนี้คุณจะได้รับข้อดีของการเปรียบเทียบสตริงที่ถูกต้องโดยไม่ต้องใช้คอลัมน์ไบนารี
เบ็น

2
@AndreFigueiredo SQL_Latin1_General_CP1_CS_ASไม่เป็นที่รู้จักใน MySQL latin1_general_csเป็นที่รู้จักกันคืออะไร
Gumbo

1
ฉันชอบที่จะมีความหมายที่นี่สำหรับสิ่งที่2, 2aและ2yหมายถึงขั้นตอนวิธีการและรูปแบบคร่ำเครียด ฉันไม่สามารถหาคำตอบง่ายๆในการค้นหาได้
jocull

2
@Neon ปัญหาคือคุณอาจเปรียบเทียบแฮชที่แตกต่างกันให้เท่ากัน หากคุณระบุอย่างชัดเจนว่าเป็นคอลัมน์ไบนารี (หรือ VARCHAR ที่มีการจัดเรียงขวา) คุณจะไม่เสี่ยงต่อการเปลี่ยนแปลงการตั้งค่าบางอย่างที่ทำให้เป็นการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ นอกจากนี้ยังทำให้ความตั้งใจของคุณชัดเจนยิ่งขึ้นซึ่งโดยทั่วไปเป็นเรื่องดี - คุณกำลังจัดเก็บข้อมูลไบนารี คุณควรเก็บมันไว้เป็นข้อมูลไบนารี
คดีฟ้องร้องกองทุนโมนิก้า

52

แฮช Bcrypt สามารถเก็บไว้ในBINARY(40)คอลัมน์

BINARY(60)เป็นคำตอบอื่น ๆ ที่แนะนำว่าเป็นตัวเลือกที่ง่ายและเป็นธรรมชาติที่สุด แต่ถ้าคุณต้องการเพิ่มประสิทธิภาพการจัดเก็บให้ได้มากที่สุดคุณสามารถประหยัด 20 ไบต์ได้โดยการแยกส่วนประกอบของแฮช ฉันทำเอกสารนี้ให้ละเอียดยิ่งขึ้นใน GitHub: https://github.com/ademarre/binary-mcf

แฮช Bcrypt ตามโครงสร้างที่เรียกว่ารูปแบบโมดูลเข้ารหัส (MCF) Binary MCF (BMCF) ถอดรหัสการแทนค่าแฮชแบบข้อความเหล่านี้เป็นโครงสร้างไบนารีขนาดกะทัดรัดยิ่งขึ้น ในกรณีของ Bcrypt แฮชไบนารีที่ได้คือ 40 ไบต์

Gumbo ทำงานได้ดีในการอธิบายองค์ประกอบสี่อย่างของแฮช Bcrypt MCF:

$<id>$<cost>$<salt><digest>

การถอดรหัส BMCF เป็นดังนี้:

  1. $<id>$ สามารถแสดงใน 3 บิต
  2. <cost>$, 04-31 สามารถแสดงได้ใน 5 บิต รวมกันเป็น 1 ไบต์
  3. เกลือที่มีความยาว 22 ตัวอักษรเป็นค่าพื้นฐานที่ไม่ได้มาตรฐาน 64 บิตจาก 128 บิต การถอดรหัส Base-64 ให้ผลตอบแทน 16 ไบต์
  4. แฮชย่อย 31 ตัวอักษรสามารถถอดรหัสได้ 64 ฐานเป็น 23 ไบต์
  5. รวมทั้งหมด 40 ไบต์: 1 + 16 + 23

คุณสามารถอ่านเพิ่มเติมได้ที่ลิงค์ด้านบนหรือตรวจสอบการใช้งาน PHP ของฉันได้ที่ GitHub


49
ต้นทุนของฟิลด์ที่ยาวนานกว่า: 20 ไบต์คูณแม้แต่ล้านเรคคอร์ด +: 20MB เมื่อคุณไปถึงหนึ่งล้านเรคคอร์ด + ค่าใช้จ่ายของการใช้ฟิลด์ที่สั้นลงอย่างไม่ถูกต้องในสาขาความปลอดภัยและวิศวกรรมที่ซับซ้อนสูง: $$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$ คุณทำคณิตศาสตร์
Kzqai

6
@Kzqai อย่างที่ฉันพูดคอลัมน์ขนาดใหญ่ขนาด 60 ไบต์นั้นเป็นตัวเลือกที่เป็นธรรมชาติที่สุด แต่วิธีที่ใช้ในการติดตามประสิทธิภาพการจัดเก็บข้อมูลนั้นขึ้นอยู่กับโครงการ ตัวอย่างเช่นเป็นเรื่องปกติที่จะพยายามปรับให้พอดีกับฐานข้อมูลทั้งหมดในหน่วยความจำและที่นี่ 20 MB และอีก 20 ที่สามารถเพิ่มขึ้นอย่างรวดเร็วในสภาพแวดล้อมที่ จำกัด หน่วยความจำ
Andre D

10
ตัวอย่างของคุณฟีดเข้าสู่จุดของฉัน --- หากคุณต้องการใส่ฐานข้อมูลลงในหน่วยความจำให้ทำการเพิ่มประสิทธิภาพคอลัมน์อื่น ๆ ก่อนที่จะแตะที่คอลัมน์หน่วยเก็บข้อมูล bcrypt --- ถ้าคุณปรับให้ทุกคอลัมน์อื่น ๆ มีองศาที่บ้าและมีเพียงคอลัมน์ bcrypt hash ที่เหลืออยู่ให้รับหน่วยความจำอีกกิกะไบต์สำหรับ bcrypt --- ถ้าคุณทำทั้งสองอย่างข้างต้น ... ... หยุดคุณยังไม่ได้ปรับปรุงคอลัมน์ผลไม้แขวนลอยทุก ๆ คอลัมน์และคุณจะยุ่งกับระบบความปลอดภัยเข้ารหัสลับที่ผ่านการทดสอบแล้วซึ่งใช้งานได้และแทนที่ มันมีระบบการปลูกที่ซับซ้อนมากขึ้นและมีโอกาสเกิดความล้มเหลวในการนำไปใช้
Kzqai

11
@Kzqai ไม่มีความเสี่ยงในการลดความปลอดภัยของห้องสมุด Bcrypt ของคุณที่นี่ มันเป็นการเข้ารหัสข้อมูลที่จะเลิกทำการเรียกคืนจากที่เก็บข้อมูลก่อนการตรวจสอบรหัสผ่าน นี่ไม่ใช่อาณาเขต "ไม่ต้องหมุน crypto ของคุณเอง"
Andre D

1
คำอธิบายที่ดี :) แม้ว่าคำอธิบายของคุณจะให้ความคิดที่ยอดเยี่ยม แต่ฉันต้องการไปกับ 60 ตัวอักษรแม้กระทั่ง 100 ตัวอักษรเพื่อความปลอดภัย การอภิปรายที่ดีเช่นกัน @Kzqai และ AndreD
Naveen Kumar V

23

หากคุณกำลังใช้ PHP password_hash()กับPASSWORD_DEFAULTอัลกอริทึมในการสร้างแฮช bcrypt (ซึ่งฉันคิดว่าเป็นคนที่อ่านคำถามนี้เป็นจำนวนมาก) โปรดจำไว้ว่าในอนาคตpassword_hash()อาจใช้อัลกอริทึมที่แตกต่างกันเป็นค่าเริ่มต้น ส่งผลต่อความยาวของแฮช (แต่อาจไม่จำเป็นต้องนานกว่านี้)

จากหน้าคู่มือ:

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

ใช้ bcrypt แม้ว่าคุณจะมีผู้ใช้ 1 พันล้านคน (นั่นคือคุณกำลังแข่งขันกับ facebook) เพื่อเก็บรหัสผ่าน 255 ไบต์ไว้มันจะมีเพียง ~ 255 GB ของข้อมูล - เกี่ยวกับขนาดของฮาร์ดไดรฟ์ SSD ขนาดเล็ก ไม่น่าเป็นไปได้อย่างยิ่งที่การจัดแฮชรหัสผ่านจะเป็นคอขวดในแอปพลิเคชันของคุณ อย่างไรก็ตามในโอกาสที่พื้นที่เก็บข้อมูลเป็นปัญหาด้วยเหตุผลบางอย่างคุณสามารถใช้PASSWORD_BCRYPTเพื่อบังคับpassword_hash()ใช้ bcrypt แม้ว่าจะไม่ใช่ค่าเริ่มต้นก็ตาม เพียง แต่อย่าลืมติดตามข่าวสารเกี่ยวกับช่องโหว่ที่พบใน bcrypt และตรวจสอบบันทึกย่อประจำรุ่นทุกครั้งที่มีการเปิดตัวเวอร์ชัน PHP ใหม่ หากอัลกอริทึมเริ่มต้นมีการเปลี่ยนแปลงจะเป็นการดีที่จะตรวจสอบสาเหตุและทำการตัดสินใจอย่างชาญฉลาดว่าจะใช้อัลกอริทึมใหม่หรือไม่


20

ฉันไม่คิดว่ามีกลอุบายใด ๆ ที่คุณสามารถเก็บสิ่งนี้ไว้ได้เช่นเดียวกับ MD5 แฮช

ฉันคิดว่าทางออกที่ดีที่สุดของคุณคือเก็บไว้ให้นานCHAR(60)ที่สุดเท่าที่จะนาน 60 ตัวอักษร


แม้ว่าบันทึกเอกสาร PHP ที่คอลัมน์ควรจะสามารถเก็บข้อมูลมากขึ้นสำหรับรุ่นในอนาคต ...
จูเลียนเอฟ Weinert

16
ไม่มีเหตุผลที่จะชุบทอง หากซอฟต์แวร์ที่คุณใช้ต้องการหกสิบไบต์ให้จัดสรรหกสิบไบต์ หากมีการเปิดตัวซอฟต์แวร์ของคุณในอนาคตที่เปลี่ยนแปลงสิ่งนี้คุณสามารถกังวลได้เมื่อมีการเปิดตัวซอฟต์แวร์นั้น คุณไม่ควรติดตั้งโปรแกรมปรับปรุงที่มีการเปลี่ยนแปลงการทำงานโดยอัตโนมัติ
Tyler Crompton
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.