คำแนะนำนี้เหมาะสำหรับนักพัฒนาซอฟต์แวร์ และผู้ดูแลระบบไอทีเป็นสำคัญ สำหรับบุคคลทั่วไปแนะนำให้อ่าน ตั้งรหัสผ่านอย่างไรให้ปลอดภัย แทนนะครับ
จริงๆ เจอเคสในการเก็บรหัสผ่านเป็นแบบ plaintext หรือเก็บรหัสผ่านแล้วสามารถย้อนกลับเป็น plaintext เดิมๆ ได้โดยง่าย ซึ่งผมเจอเคสแบบนี้บ่อยค่อนข้างมาก และคิดว่าควรจะเขียนเรื่องนี้สักครั้งหนึ่ง
หลักของการเก็บรหัสผ่านในฐานข้อมูลนั้น
1. ไม่ใช่การเก็บตัวรหัสผ่านจริงๆ ลงไป
2. ไม่ควรใช้การจัดเก็บรหัสผ่านที่สามารถย้อนกลับมาเป็นรหัสผ่านต้นทางได้ ไม่ว่าจะทางใดก็ทางหนึ่ง
หลักง่ายๆ ก่อน 2 ข้อนี้ ทำให้เกิดวิธีคิดในการเก็บรหัสผ่านเพียงแค่ hash (หรือบางเอกสารเรียก fingerprint หรือค่า message digest) ของรหัสผ่านนั้นๆ กล่าวคือ การกระทำใดๆ ในตัวระบบ ต้องไม่มีความสามารถที่สามารถย้อนกลับวิธีการได้มาซึ่งข้อมูล hash ได้ (Non-invertible) เพื่อเป็นการป้องกันรหัสผ่านที่แท้จริงจากการนำไปใช้งานได้ทันทีหลังจากระบบถูก hack แล้วถูก dump ข้อมูลเหล่านี้ออกไป เพราะแม้ว่ารหัสผ่านที่ถูกนำออกไปจะเป็นเพียงแค่ hash ของข้อมูล แต่การโจมตีแบบ rainbow table attacks ก็สามารถถอดรหัสผ่านใดๆ ที่เป็นเพียงแค่ค่า hash ย้อนกลับมาเป็นรหัสผ่านเดิมได้ไม่ช้าก็เร็ว เพราะแม้มีความเป็นไปได้น้อยมากๆ แต่ก็ถือว่ามีความเสี่ยง
หลักการขอ rainbow table attacks คล้ายๆ การ brute force รหัสผ่านโดยทั่วไป แต่เป็นการกระทำต่อ hash เป็นหลัก โดยสุ่มชุดข้อมูลที่คิดว่าเป็นรหัสผ่าน มาเข้าขั้นตอน hash ที่เป็นที่นิยมใช้ให้ได้ซึ่งค่า hash แล้วเอาชุดข้อมูลดังกล่าว ใส่ลงในฐานข้อมูล rainbow tableไว้ แล้วเมื่อมีการ hack ระบบเกิดขึ้นแล้วได้ชุดข้อมูล hash ที่ dump ออกมา ก็เอาไปทดสอบเทียบกับชุดข้อมูลในฐานข้อมูลที่สร้างไว้ก่อนหน้านี้ ก็จะย้อนกลับมาได้ว่า ข้อมูล plaintext ที่เป็นส่วนย้อนกลับของ hash มีค่าใด
หากแต่การปรับปรุงและใช้ขั้นตอนของการใดมาซึ่งค่า hash ที่ยาวขึ้น มีความซับซ้อนมากขึ้น ไม่ใช้ขั้นตอนตามมาตรฐานเพียงอย่างเดียว มีการผสมชุดข้อมูลอื่นๆ ที่สร้างขึ้นจากระบบด้วยแล้วการถูก rainbow table attacks ก็มีความเสี่ยงน้อยลงไปอีก
โดยด้านล่างเป็นลักษณะของฟังค์ชั่นโดยทั่วไปที่มักใช้กัน
[hash] = [salt] + [hash_function([salt] + [password])]
[salt] = เป็นการสุ่มจากวิธีการสุ่มใดๆ ที่ปลอดภัยที่สุดเท่าที่ทำได้ คำแนะนำคือควรใช้วิธีการสุ่มจากฟังค์ชั่นแบบ Cryptographically Secure Pseudo-Random Number Generator (CSPRNG)
[hash_function] ก็เลือกว่าจะเอาตัวไหนก็ได้ แต่ที่ใช้ๆ กันก็ แต่พวก MD5 หรือ SHA-1 ที่มันหาง่าย แต่มันก็แข็งแรงน้อยลงมากๆ ไม่แนะนำ และเดี่ยวนี้มันพูดกันที่ระดับ SHA-2 กันไปแล้ว (SHA-2 = SHA-256 หรือ SHA-512)
จริงๆ สูตรฟังค์ชันด้านบนนั้น มีชุดคำสั่งสำเร็จรูปที่นิยมกันที่ชื่อ PBKDF2 เพราะมีการกำหนดชุดการฟังคัชันในการ hash, จำนวนรอบในการสุ่ม และกำหนดความยาวของ hash ได้หลากหลาย ทำให้ยากมากขึ้นในการได้มากซึ่ง plaintext ที่แท้จริงจาก rainbow table
hash= PBKDF2(hash_function_name, password, salt, iterations, length)
ส่วนการใช้งานตอนเปรียบเทียบรหัสผ่านที่ผู้ใช้กรอกเข้ามา กับ hash ที่ระบบเก็บไว้ ก็ตรงไปตรงมา ก็แค่นำรหัสผ่านที่ผู้ใช้กรอกเข้ามาเข้าวิธีการเดิมที่ได้มาซึ่ง hash เดิม แล้วนำมาเปรียบเทียบว่าตรงกันหรือไม่
โดยการทำแบบนี้ “ช่วยให้ผู้ใช้งานทั้งระบบยังพอมีเวลาในการปรับเปลี่ยนรหัสผ่านชุดใหม่ได้ใน ระยะเวลาที่น่าจะปลอดภัยมากที่สุด” เพราะหากว่ากันตามความเป็นจริงแล้ว ในวงการด้านการเข้ารหัสข้อมูลนั้น ทราบกันดีว่า ไม่มีวิธีการใดอะไรที่สามารถเข้ารหัสข้อมูลได้ปลอดภัยที่สุดไปตลอดกาล เพราะเทคโนโลยีด้านการคำนวนของ CPU/GPU นั้นเร็วมากขึ้นทุกวัน การจัดเก็บข้อมูลที่ปลอดภัยในช่วงเวลาหนึ่งอาจไม่ปลอดภัยในอีกไม่กี่ปีต่อมา เพราะเราสามารถแกะ และคำนวนหาค่าต่างๆ หรือสุ่มชุดข้อมูลต่างๆ ได้เร็วและหลายหลากขึ้น ตาม CPU/GPU ที่เร็วขึ้นเพื่อมาสร้าง rainbow table ได้ทุกขณะ
ทั้งหมดที่กล่าวมา เป็นเพียงแค่การทำความเข้าใจเริ่มต้นของแนวคิดในการเก็บรหัสผ่านในระบบเท่านั้น แนะนำให้อ่านข้อมูลเพิ่มเติมจาก keyword ต่างๆ ในบทความนี้เพิ่มเติม เพื่อทำความเข้าใจในทุกขั้นตอนต่อไป
อ้างอิง
- Password Storage Cheat Sheet – Open Web Application Security Project (OWASP)
- Salted Password Hashing – Doing it Right