Password Salting: เค็มแต่ดี
นี่ไม่ใช่การโฆษณายาสีฟันแต่อย่างใด แต่ในครั้งนี้เราจะมาอธิบายเรื่องของการเก็บรหัสผ่านลงในฐานข้อมูลอย่างปลอดภัยด้วยการใส่เกลือเข้าไป (Salting)
รหัสผ่านเป็นตัวอย่างหนึ่งของข้อมูลผู้ใช้ที่มีความสำคัญเป็นอย่างมาก ไม่ว่าจะเป็นการส่งรหัสผ่านมาจากหน้าเว็บหรือการจัดเก็บรหัสผ่านลงใน Database แน่นอนว่าถ้าเราต้องการจะจัดเก็บรหัสผ่านลงใน Database เพราไม่ควรที่จะเก็บรหัสผ่านลงไปตรงๆ เพราะถ้าหากข้อมูลนั้นหลุดออกไป คนอื่นก็จะเข้าถึงรหัสผ่านของผู้ใช้ได้เลย
วิธีที่ดีกว่าในการจัดเก็บรหัสผ่านก็คือการ Hash รหัสผ่านก่อนที่จัดเก็บลง Database โดยถ้าหากเกิดข้อมูลหลุดออกไปจริง รหัสผ่านที่หลุดออกไป ก็ไม่ได้เป็นรหัสผ่านจริงๆ แต่นั้นก็ยังไม่ใช่สิ่งที่ดีที่สุด เราจะมาดูกันว่าทำไม
![](https://blog.sethanantp.com/content/images/2021/08/Screen-Shot-2564-08-18-at-10.06.56.png)
การ Hash Password แบบปกติ
การที่เราจะ Hash Password ได้นั้น เราก็ต้องเลือก cryptographic hash function ก่อนว่าเราจะใช้อะไรเป็น function ในการเข้ารหัสของเรา โดยตัวที่เราใช้กันบ่อยๆก็คือ ตระกูล SHA-2, SHA3 และอื่นๆ โดย function เราที่เลือกมานั้นต้องมีความปลอดภัยและมีความยาวมากพอที่จะกันการ Brute-force ได้
ลักษณะอย่างหนึ่งของ cryptographic hash function คือค่าที่ได้จากการ Hash จะเป็นค่าเดิมเสมอ ถ้าหากว่า input ที่ใส่เข้าไปเป็นค่าเดียวกัน อย่างเช่น ถ้าเรามีรหัสผ่านคือคำว่า password
เมื่อทำการ hash ด้วย SHA-1 (มันไม่ปลอดภัยแล้วแต่ว่ามันสั้นดี) ค่าผลลัพธ์ที่ได้ก็คือ 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
โดยไม่ว่าเราจะทำการ hash คำว่า password
สักกี่รอบ ผลที่ได้ก็จะเป็นค่าเดิม
![](https://blog.sethanantp.com/content/images/2021/08/Screen-Shot-2564-08-18-at-10.08.42.png)
จากตารางน้ีจะเห็นได้ว่า alice และ bob ใช้รหัสผ่านเป็นคำว่า password
เหมือนกัน ดังนั้น Hash ที่ออกมาจึงเหมือนกัน
จุดนี้จึงเป็นจุดอ่อนของการใช้แค่ hash function เพียงอย่างเดียว เนื่องจากว่าถ้าหากข้อมูลหลุดออกไป ถึงแม้ว่ารหัสผ่านจะถูก hash อยู่ แต่ hacker สามารถสร้างตารางคำศัพท์และรหัสผ่านที่คนใช้กันทั่วไปอย่างเช่น password, 12345678, หรืออะไรก็ตาม และทำการ hash ข้อมูลในตารางนั้น เพื่อนำมาเทียบกับค่า hash ของข้อมูลที่หลุดออกมา เมื่อพบว่าตรงกัน hacker ก็จะสามารถรู้รหัสผ่านได้
![](https://blog.sethanantp.com/content/images/2021/08/Screen-Shot-2564-08-18-at-10.09.26.png)
ตารางด้านบนนี้เป็นตัวอย่างตารางของรหัสผ่านที่คนใช้บ่อยและคำศัพท์ โดยรหัสผ่านทางด้านซ้ายจะถูก hash ไว้แล้วแล้วเก็บไว้ใน colume ด้านขวา โดยเราจะเห็นได้ว่า hashed_password ของ alice และ bob ตรงกับคำว่า password
และ ของ dom จะตรงกับคำว่า family
ดังนั้น hacker จึงสามารถที่จะรู้รหัสผ่านจริง ผ่านการเทียบค่าค่า hash ที่ pre-compute ไว้ก่อนแล้ว
รหัสผ่านผสมเกลือ (Salting)
เราเห็นไปแล้วว่าการเก็บรหัสผ่านด้วยการ hash อย่างเดียวนั้นยังมีจุดอ่อนอยู่ ดังนั้นจึงมีการคิดเทคนิคอีกตัวขึ้นมาก็คือการใส่ Salt ลงไป
Salt คือค่าหนึ่ง ที่ถูกสร้างขึ้นมาแบบ random และมันจะถูกนำไป วางไว้อยู่ด้านหน้าหรือด้านหลังของรหัสผ่านจริงๆ แล้วค่อยนำรหัสผ่านและ salt ที่รวมกันแล้วนั้น นำไป hash ทั้งก้อน
ตัวอย่างเช่นถ้าหากเราใช้ค่าของ salt เป็น veryrandomstringvalue
- คำว่า
password
ที่ผ่าน SHA-1 จะได้เป็น5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
- คำว่า
password
ที่รวมกับ salt (ใส่ . เพื่อแยก salt กับรหัสผ่านจริง) กลายเป็นveryrandomstringvalue.password
และเมื่อนำไปผ่าน SHA-1 จะได้เป็น7d69d76dc3eb788282f6a7d324825eabb5d47a8f
จะเห็นได้ว่าการเพิ่ม salt เข้าไปก่อนที่จะ hash ทำให้ค่าที่ hash ออกมาต่างกับการ hash แค่รหัสผ่านอย่างเดียว โดยจะเป็นการเพิ่มความแตกต่างให้กับรหัสผ่านที่เก็บในระบบของเรา ดังนั้นการที่ hacker จะทำการสร้างตารางของ hashed_password แล้วนำมาเทียบกับข้อมูลที่หลุดออกมา ก็จะทำได้ยากขึ้นเพราะ ถ้าหาก hacker ไม่รู้ว่า salt คือค่าอะไรแล้ว ผลการ hash รหัสผ่านของ hacker ก็จะไม่มีวันตรงกับ hashed_password ที่หลุดออกมานั้นเอง
![](https://blog.sethanantp.com/content/images/2021/08/Screen-Shot-2564-08-18-at-10.13.44.png)
ตารางนี้เป็นการเก็บรหัสผ่านที่ถูกรวมเข้ากับ salt ก่อนที่จะนำไป hash ดังนั้นถ้า hacker ใช้ตารามเดิม(จากด้านบน) ในการเทียบรหัสผ่านที่ hash แล้ว เขาก็จะไม่สามารถหารหัสผ่านที่ค่า hash ตรงกันได้เนื่องจาก salt ที่เราเพิ่มเข้าไป ดังนั้น hacker จึงจำเป็นที่จะต้องรู้ salt เพื่อนำ salt นั้นไปรวมกับ common password เพื่อสร้างตารางใหม่ที่จะนำมาเทียบกับข้อมูลที่หลุดออกไปอีกทีนึง
![](https://blog.sethanantp.com/content/images/2021/08/salt-bae-sprinkling-salt.gif)
เมื่อ Salt ไม่จำเป็นต้องมีแค่หนึ่ง
ถึงเราจะมีการใช้ salt เพื่อเพิ่มความแตกต่างให้กับรหัสผ่านที่เก็บใน database ของเรา แต่เรายังคงสังเกตเห็นได้ว่า ผู้ใช้บางคนที่ใช้รหัสผ่านเหมือนกันนั้น จะมีค่า hash ของรหัสผ่านที่เหมือนกัน เนื่องจากรหัสผ่านเป็นอันเดียวกัน และค่า salt ที่เราใช้ก็เหมือนกันทั้งหมด ดังนั้นอีกขั้นหนึ่งในการเก็บรหัสผ่านให้ปลอดภัยมากขึ้นก็คือการใช้ salt ที่แตกต่างกันในการ hash ทุกครั้ง
หลักการของการสร้าง hash ใหม่ทุกครั้งก็จะเป็นไปตามนี้
- สร้างค่า salt ใหม่ขึ้นมาแบบสุ่ม
- นำค่า salt ไปรวมเข้ากับรหัสผ่านจริง
- เอา salt และรหัสผ่านจริงที่รวมกันแล้วไปเข้า hash function
- เก็บทั้งรหัสผ่านที่ hash แล้วในขั้นตอนที่ 3 และ ค่า salt จากขั้นตอนที่ 1 เก็บลงไปใน database
ยกตัวอย่างเช่น เราสร้างค่า salt ใหม่ขึ้นมาเป็น qwertyuiop
และมีรหัสผ่านคือ password
เมื่อรวม 2 อันน้ีจะได้เป็น qwertyuiop.password
(ใส่ . เพื่อแยก salt กับรหัสผ่านจริง) โดยเมื่อนำไป hash ด้วย SHA-1 จะได้ cdda291710164710fc9a69ccb50e3f439335f233
และขั้นตอนสุดท้ายก็คือการเอา salt และ hashed_password นำไปเก็บคู่กันใน database
![](https://blog.sethanantp.com/content/images/2021/08/Screen-Shot-2564-08-18-at-10.14.33.png)
จากการใช้ salt ที่แตกต่างกันไปในแต่ล่ะครั้ง จะเห็นได้ว่า ถึงแม้ bob และ alice จะใช้รหัสผ่านเป็นคำว่า password
เหมือนกัน แต่ค่า hash ที่ออกมานั้นต่างกัน เนื่องจาก salt ที่ใส่เข้าไปนั้นแตกต่างกัน ทำให้ hacker ต้องใช้เวลาสร้างตารางเพิ่มจำนวนมาก (ตามจำนวน salt ที่ใช้) เพื่อที่จะหารหัสผ่านตามวิธีปกติ ดังนั้นวิธีนี้จึงเป็นวิธีที่แนะนำให้การเก็บรหัสผ่าน
Salt is unencrypted, secure or not ?
หลายคนอ่านมาถึงจุดนี้ ก็อาจจะสงสัยว่า การที่เราเก็บ salt ลงไปเฉยๆ เลย มันจะมีผลต่อความปลอดภัยรึเปล่า แต่จริงๆ ก็คือ ถึงแม้ว่า hacker จะรู้ salt แต่ถ้า hacker ต้องการจะนำ salt ไปรวมเข้ากับรหัสผ่านที่คนใช้บ่อย แล้วทำการหาค่า hash นั้น ผลที่ได้ก็คือการที่ต้องสร้างตารางเก็บ hashed_password จำนวนมาก เนื่องจากรหัสผ่านแต่ล่ะอันก็ใช้ salt ที่ไม่เหมือนกัน ดังนั้นการสร้าง table เพื่อทำการ attack จึงเป็นเรื่องที่เปลือง computational power เอามากๆ
คิดดูว่าถ้าเรามีผู้ใช้ทั้งหมด 10,000 คน เท่ากับว่าเรามี salt 10,000 ชุด ดังนั้นถ้า hacker ต้องการจะเทียบ hashed_password กับ รหัสผ่านที่คนใช้บ่อย (assume ว่ามี 1,000 password) ก็จะต้องทำการเรียกใช้ hash function ทั้งหมด 10,000 x 1,000 = 10 ล้านครั้ง ยิ่งถ้าหากเราใช้ cryptographic hash function ที่ทำงานช้าแล้ว การกระทำแบบนี้ก็สามารถทำได้ยากมาก