Password Salting: เค็มแต่ดี

นี่ไม่ใช่การโฆษณายาสีฟันแต่อย่างใด แต่ในครั้งนี้เราจะมาอธิบายเรื่องของการเก็บรหัสผ่านลงในฐานข้อมูลอย่างปลอดภัยด้วยการใส่เกลือเข้าไป (Salting)

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

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

เราจะใช้ตารางข้อมูลนี้ในตัวอย่างการ Hash

การ 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 สักกี่รอบ ผลที่ได้ก็จะเป็นค่าเดิม

ตารางข้อมูลที่ผ่านการ hash รหัสผ่านด้วย SHA-1

จากตารางน้ีจะเห็นได้ว่า alice และ bob ใช้รหัสผ่านเป็นคำว่า password เหมือนกัน ดังนั้น Hash ที่ออกมาจึงเหมือนกัน

จุดนี้จึงเป็นจุดอ่อนของการใช้แค่ hash function เพียงอย่างเดียว เนื่องจากว่าถ้าหากข้อมูลหลุดออกไป ถึงแม้ว่ารหัสผ่านจะถูก hash อยู่ แต่ hacker สามารถสร้างตารางคำศัพท์และรหัสผ่านที่คนใช้กันทั่วไปอย่างเช่น password, 12345678, หรืออะไรก็ตาม และทำการ hash ข้อมูลในตารางนั้น เพื่อนำมาเทียบกับค่า hash ของข้อมูลที่หลุดออกมา เมื่อพบว่าตรงกัน hacker ก็จะสามารถรู้รหัสผ่านได้

ตารางที่ hacker สามารถสร้างขึ้นมาโดยการ pre-computing ค่า hash ของรหัสผ่านที่คนใช้บ่อย

ตารางด้านบนนี้เป็นตัวอย่างตารางของรหัสผ่านที่คนใช้บ่อยและคำศัพท์ โดยรหัสผ่านทางด้านซ้ายจะถูก 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 ที่หลุดออกมานั้นเอง

ตารางข้อมูลที่รหัสผ่านถูกรวมเข้ากับ salt ก่อนนำไป hash ด้วย SHA-1

ตารางนี้เป็นการเก็บรหัสผ่านที่ถูกรวมเข้ากับ salt ก่อนที่จะนำไป hash ดังนั้นถ้า hacker ใช้ตารามเดิม(จากด้านบน) ในการเทียบรหัสผ่านที่ hash แล้ว เขาก็จะไม่สามารถหารหัสผ่านที่ค่า hash ตรงกันได้เนื่องจาก salt ที่เราเพิ่มเข้าไป ดังนั้น hacker จึงจำเป็นที่จะต้องรู้ salt เพื่อนำ salt นั้นไปรวมกับ common password เพื่อสร้างตารางใหม่ที่จะนำมาเทียบกับข้อมูลที่หลุดออกไปอีกทีนึง

เมื่อ Salt ไม่จำเป็นต้องมีแค่หนึ่ง

ถึงเราจะมีการใช้ salt เพื่อเพิ่มความแตกต่างให้กับรหัสผ่านที่เก็บใน database ของเรา แต่เรายังคงสังเกตเห็นได้ว่า ผู้ใช้บางคนที่ใช้รหัสผ่านเหมือนกันนั้น จะมีค่า hash ของรหัสผ่านที่เหมือนกัน เนื่องจากรหัสผ่านเป็นอันเดียวกัน และค่า salt ที่เราใช้ก็เหมือนกันทั้งหมด ดังนั้นอีกขั้นหนึ่งในการเก็บรหัสผ่านให้ปลอดภัยมากขึ้นก็คือการใช้ salt ที่แตกต่างกันในการ hash ทุกครั้ง

หลักการของการสร้าง hash ใหม่ทุกครั้งก็จะเป็นไปตามนี้

  1. สร้างค่า salt ใหม่ขึ้นมาแบบสุ่ม
  2. นำค่า salt ไปรวมเข้ากับรหัสผ่านจริง
  3. เอา salt และรหัสผ่านจริงที่รวมกันแล้วไปเข้า hash function
  4. เก็บทั้งรหัสผ่านที่ hash แล้วในขั้นตอนที่ 3 และ ค่า salt จากขั้นตอนที่ 1 เก็บลงไปใน database

ยกตัวอย่างเช่น เราสร้างค่า salt ใหม่ขึ้นมาเป็น qwertyuiopและมีรหัสผ่านคือ password

เมื่อรวม 2 อันน้ีจะได้เป็น qwertyuiop.password (ใส่ . เพื่อแยก salt กับรหัสผ่านจริง) โดยเมื่อนำไป hash ด้วย SHA-1 จะได้ cdda291710164710fc9a69ccb50e3f439335f233 และขั้นตอนสุดท้ายก็คือการเอา salt และ hashed_password นำไปเก็บคู่กันใน database

ตารางข้อมูลที่รหัสผ่านถูกรวมเข้ากับ salt ที่แตกต่างกัน และนำไป hash ด้วย SHA-1

จากการใช้ 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 ที่ทำงานช้าแล้ว การกระทำแบบนี้ก็สามารถทำได้ยากมาก

Sources: