บทความนี้จะแนะนำคุณให้รู้จักกับ Bcrypt หนึ่งในอัลกอริทึมการทำ Password Hashing ที่แข็งแกร่งและเป็นที่ยอมรับ เพื่อใช้ในการ Hash รหัสผ่านในภาษา Python ซึ่งจะช่วยให้ข้อมูลรหัสผ่านของผู้ใช้ของคุณปลอดภัยแม้ในกรณีที่ฐานข้อมูลถูกเจาะ
ทำไมต้อง Hash รหัสผ่าน และทำไมต้องเป็น Bcrypt?
การเก็บรหัสผ่านแบบ Plain Text นั้นอันตรายอย่างยิ่ง หากฐานข้อมูลถูกบุกรุก รหัสผ่านของผู้ใช้จะตกอยู่ในมือของแฮกเกอร์ทันที
Hashing คือกระบวนการแปลงข้อมูล (ในที่นี้คือรหัสผ่าน) ให้เป็นชุดอักขระที่ไม่สามารถย้อนกลับไปหารหัสผ่านต้นฉบับได้ (One-way Function) ซึ่งต่างจากการเข้ารหัส (Encryption) ที่สามารถถอดรหัสกลับได้
แล้วทำไมต้องเลือก Bcrypt ล่ะ?
- ออกแบบมาเพื่อ Hash รหัสผ่านโดยเฉพาะ: Bcrypt ถูกออกแบบมาเพื่อต้านทานการโจมตีแบบ Brute-force และ Dictionary Attack โดยเฉพาะ
- Computationally Intensive: Bcrypt ถูกทำให้ทำงานช้าลงโดยใช้ "Work Factor" หรือ "Cost Factor" ยิ่งค่า Work Factor สูงเท่าไหร่ การคำนวณ Hash ก็ยิ่งใช้เวลามากขึ้นเท่านั้น การทำเช่นนี้ทำให้การโจมตีแบบเดาสุ่มทำได้ยากและใช้เวลานานมาก แม้ว่าเครื่องคอมพิวเตอร์จะทรงพลังขึ้นในอนาคต เราก็สามารถเพิ่ม Work Factor เพื่อรักษาความปลอดภัยได้
- ใช้ Salt อัตโนมัติ: Bcrypt จะสร้าง Salt (ค่าสุ่มที่ไม่ซ้ำกัน) และผนวกเข้าไปใน Hash Output โดยอัตโนมัติ ทำให้รหัสผ่านเดียวกันถูก Hash ออกมาได้ค่าที่ไม่ซ้ำกันในแต่ละครั้ง ซึ่งช่วยป้องกัน Rainbow Table Attack ได้อย่างมีประสิทธิภาพ
ในการใช้ Bcrypt ใน Python เราจะใช้ไลบรารีที่ชื่อว่า bcrypt ซึ่งคุณสามารถติดตั้งได้ผ่าน pip:
โค้ด: เลือกทั้งหมด
pip install bcrypt
ขั้นตอนแรกคือการนำรหัสผ่าน Plain Text ที่ผู้ใช้ป้อนเข้ามา (เช่น ตอนสมัครสมาชิก) มา Hash ก่อนจะเก็บลงในฐานข้อมูล
โค้ด: เลือกทั้งหมด
import bcrypt
def hash_password(password):
# เปลี่ยน password string เป็น bytes (bcrypt ทำงานได้เฉพาะ bytes)
password_bytes = password.encode('utf-8')
# สร้าง salt และ hash password
# สร้าง generates a random salt
# สามารถเพิ่มเติมได้เอง เช่น bcrypt.gensalt(rounds=14)
hashed_password = bcrypt.hashpw(password_bytes, bcrypt.gensalt())
return hashed_password
# --- ตัวอย่างการใช้งาน ---
plain_password = "MySuperSecurePassword123!"
hashed_pwd = hash_password(plain_password)
print(f"Plain Password: {plain_password}")
print(f"Hashed Password: {hashed_pwd.decode('utf-8')}") # แปลงกับเป็น string
- password.encode('utf-8'): Bcrypt ทำงานกับข้อมูลแบบ bytes ดังนั้นเราจึงต้องแปลง String ของรหัสผ่านให้เป็น bytes ก่อน
- bcrypt.gensalt(): ฟังก์ชันนี้จะสร้าง Salt แบบสุ่มที่ไม่ซ้ำกันในแต่ละครั้งที่เรียกใช้ ค่า rounds (Work Factor) เริ่มต้นคือ 12 ซึ่งเป็นค่าที่เหมาะสมสำหรับ Bcrypt ในปัจจุบัน
- bcrypt.hashpw(password_bytes, bcrypt.gensalt()): นี่คือฟังก์ชันหลักที่ใช้ในการ Hash รหัสผ่าน มันจะนำรหัสผ่านและ Salt ที่สร้างขึ้นมาใช้ในการคำนวณ Hash
เมื่อผู้ใช้พยายามเข้าสู่ระบบ คุณจะต้องนำรหัสผ่านที่ผู้ใช้ป้อนเข้ามาไปเปรียบเทียบกับ Hash ที่เก็บอยู่ในฐานข้อมูล ซึ่ง bcrypt ก็มีฟังก์ชันสำหรับทำสิ่งนี้โดยเฉพาะ
โค้ด: เลือกทั้งหมด
import bcrypt
def verify_password(plain_password, hashed_password):
# แปลง string to bytes
plain_password_bytes = plain_password.encode('utf-8')
# Verify the password
return bcrypt.checkpw(plain_password_bytes, hashed_password)
โค้ด: เลือกทั้งหมด
import bcrypt
def hash_password(password):
# เปลี่ยน password string เป็น bytes (bcrypt ทำงานได้เฉพาะ bytes)
password_bytes = password.encode('utf-8')
# สร้าง salt และ hash password
# สร้าง generates a random salt
# สามารถเพิ่มเติมได้เอง เช่น bcrypt.gensalt(rounds=14)
hashed_password = bcrypt.hashpw(password_bytes, bcrypt.gensalt())
return hashed_password
def verify_password(plain_password, hashed_password):
# แปลง string to bytes
plain_password_bytes = plain_password.encode('utf-8')
# Verify the password
return bcrypt.checkpw(plain_password_bytes, hashed_password)
# --- ตัวอย่างการใช้งาน ---
plain_password = "MySuperSecurePassword123!"
hashed_pwd = hash_password(plain_password)
if verify_password(plain_password, hashed_pwd):
print(f"Plain Password: '{plain_password}' - Login successful!")
else:
print(f"'{plain_password}' - Login failed!")
print(f"Hashed Password: {hashed_pwd.decode('utf-8')}") # แปลงกับเป็น string
- bcrypt.checkpw(plain_password_bytes, hashed_password): ฟังก์ชันนี้จะทำการ Hash รหัสผ่านที่ผู้ใช้ป้อนเข้ามาโดยใช้ Salt และ Work Factor ที่ดึงมาจาก hashed_password ที่เก็บไว้ จากนั้นจึงนำผลลัพธ์มาเปรียบเทียบกัน หากตรงกันจะคืนค่า True หากไม่ตรงจะคืนค่า False
- สำคัญ: คุณไม่จำเป็นต้องดึง Salt หรือ Work Factor ออกมาจาก Hash เอง bcrypt.checkpw จัดการให้ทั้งหมดแล้ว
การจัดการ Work Factor (Cost Factor)
Work Factor (หรือ rounds ใน bcrypt.gensalt()) เป็นตัวกำหนดความยากในการคำนวณ Hash ยิ่งค่าสูง ก็ยิ่งปลอดภัย แต่ก็ยิ่งใช้เวลาในการประมวลผลนานขึ้น
- ค่าเริ่มต้นคือ 12 ซึ่งเป็นค่าที่เหมาะสมในปัจจุบัน
- คุณสามารถปรับค่านี้ได้ หากฮาร์ดแวร์ของคุณมีประสิทธิภาพสูงขึ้น หรือหากคุณต้องการเพิ่มความปลอดภัยในอนาคต
โค้ด: เลือกทั้งหมด
import bcrypt
# Hash with a higher work factor (e.g., 14)
higher_cost_hashed_pwd = bcrypt.hashpw(b"anotherPassword", bcrypt.gensalt(rounds=14))
print(f"Hashed with cost 14: {higher_cost_hashed_pwd.decode('utf-8')}")
- b"anotherPassword" คือการทำให้เป็น bytes
- bcrypt.gensalt(rounds=14) คือ จำนวน rounds ที่ใช้ในการคำนวณ Hash
ข้อควรพิจารณา: การเพิ่ม Work Factor จะส่งผลต่อเวลาในการ Login หรือ Register เนื่องจากต้องใช้เวลาในการคำนวณ Hash นานขึ้น คุณควรทดสอบให้แน่ใจว่าค่าที่เลือกไม่ส่งผลกระทบต่อประสบการณ์ผู้ใช้มากเกินไป
สรุป
การใช้ Bcrypt ในการ Hash รหัสผ่านใน Python เป็นแนวทางปฏิบัติที่สำคัญและเป็นมาตรฐานสำหรับความปลอดภัยของข้อมูลผู้ใช้ ด้วยไลบรารี bcrypt คุณสามารถ Hash รหัสผ่านและตรวจสอบความถูกต้องได้อย่างง่ายดายและมีประสิทธิภาพ โดยไม่ต้องกังวลเรื่องการจัดการ Salt หรือ Work Factor ที่ซับซ้อน ซึ่งถูกจัดการให้อัตโนมัติโดยไลบรารีนี้
นอกจากนี้ Bcrypt ยังออกแบบมาให้ต้านทานการโจมตีแบบ Brute-force และ Rainbow Table ได้ดี โดยสามารถปรับระดับความยาก (work factor) ได้ตามความต้องการ เพื่อให้เหมาะสมกับสภาพแวดล้อมและทรัพยากรของระบบของคุณอย่างปลอดภัยและยืดหยุ่น
อ้างอิง
- https://www.mindphp.com/บทความ/31-ความร ... h-e-w.html
- viewtopic.php?f=79&t=116426
- https://www.mindphp.com/คู่มือ/73-คืออะ ... ออะไร.html
- viewtopic.php?t=105393
- https://github.com/pyca/bcrypt/