By Arnon Puitrakul - 23 พฤศจิกายน 2020
ปัจจุบัน เราใช้งานระบบคอมพิวเตอร์ในการทำอะไรกันเยอะมาก ๆ ตั้งแต่การค้นหาข้อมูล ยันเก็บเรื่องสำคัญ ๆ ที่เป็นความลับไว้ในนั้น ซึ่งการจะเข้าถึงความลับได้นั้น มันจะต้องอาศัยสิ่งที่เรียกว่า Authentication หรือสั้น ๆ ว่า Auth หรือภาษาไทยเราเรียกว่า การยืนยันตัวตน ซึ่งสิ่งที่เราใช้เอามายืนยันตัวตนกันเยอะมาก ๆ คือ การใช้ Username และ Password แต่เราเคยสงสัยกันมั้ยว่า ในโปรแกรมมันเก็บพวก Password ยังไง ให้เจ้าของโปรแกรมก็ยังไม่รู้เลยว่า Password ที่เรากรอกไปคืออะไร ?
ถ้าคิดผ่าน ๆ เราอาจจะคิดว่า Password ที่เรากรอกเข้าไปในระบบมันถูกเก็บตรง ๆ แบบนั้นเลย เช่น เรากรอกไปว่า 1234 ในระบบก็จะเก็บ 1234 ลองคิดต่อว่า ถ้าเกิดนักพัฒนาทำแบบนี้ไปมันจะเกิดอะไรขึ้น
อย่างแรกคือ ถ้าเกิดว่าตัวระบบ หรือฐานข้อมูลของโปรแกรมถูกเจาะขึ้นมา คนที่ลักลอบเข้ามาก็จะได้รหัสผ่านเราไปได้แบบง่าย ๆ เลย อันนี้ไม่ดีแน่ ๆ เพราะบางคน ก็มักง่ายแหละ ใช้รหัสผ่านเดียวกันในทุก ๆ บริการที่เราใช้งาน ถ้าคนที่เจาะเข้ามาได้รหัสผ่านไป เขาก็เข้าได้ทุกบริการที่เราใช้งานอยู่ได้เลย ไม่ปลอดภัยมาก ๆ
และอย่าคิดว่า เรื่องพวกนี้มันเกิดขึ้นยากมาก ถ้าเราลองไปอ่านข่าวหลาย ๆ ปีที่ผ่านมา ผู้ให้บริการใหญ่ ๆ หลาย ๆ รายก็โดนกันมาทั้งนั้น อย่างล่าสุดเมื่อไม่กี่วันก่อน Lazada ก็โดนไปอีกดอก หรือแม้แต่บริษัทใหญ่ระดับโลกอย่าง Linkedin ก็ไม่รอดในปี 2016 เช่นกัน ดังนั้น ไม่ว่าเราจะใช้บริการไหน ผู้ให้บริการก็มีโอกาสที่จะโดนเจาะ หรือล้วงข้อมูลทั้งนั้น
นอกจากนั้น การที่เก็บ Password ตรง ๆ ยังทำให้เจ้าของโปรแกรมรู้อีกว่า เราใช้ Password อะไร และถ้าเจ้าของโปรแกรมเกิดวันนึงตื่นมาโป๊ะบ๊ะอะไรขึ้นมา อยากจะเอา Password เราไปขาย เขาก็ทำได้ จาก 2 เหตุผลนี้ทำให้การเก็บ Password ตรง ๆ ไม่ใช่เรื่องดีมาก ๆ
คำถามคือ แล้วถ้าเราไม่เก็บ Password ตรง ๆ แล้วเราจะเก็บยังไงละ เพราะมันไม่ได้แค่เก็บ แต่ต้องเป็นตัวที่ใช้ในการยืนยันตัวด้วย จริง ๆ ก็คือ เขาใช้สิ่งที่เรียกว่าการทำ Hashing กัน
โดยที่ Hash เป็น Function ทางคณิตศาสตร์ประเภทนึงที่มีความสัมพันธ์แบบ Many-to-one โดยที่เราจะใส่ Password ของเราลงไป และ ผ่าน Function ออกมาเป็นค่าที่ผ่านการ Hashed แล้ว และนำไปเก็บ
ดังนั้นเวลาเราจะยืนยันตัวตน โปรแกรมก็จะแปลงรหัสผ่านของเราด้วย Hash Function และ นำไปเทียบกับค่า Hash ที่เก็บไว้ตอนแรก ถ้าเหมือนกันก็คือใช่แล้ว ถูกต้อง แต่ถ้าไม่เหมือนก็คือ ไม่ผ่านจ้าา อะไรแบบนั้น
เมื่อกี้เราบอกว่า Hash Function มีความสัมพันธ์แบบ Many-to-one ระหว่าง Password และ Hashed Value ทำให้ 1 Hashed Value มาจากได้หลาย Password กล่าวคือ เราอาจจะมี Password บางตัวที่เมื่อ Hash แล้วมันได้ผลลัพธ์เหมือนกับเราใช้ Password อีกตัว Hash เราเรียกอาการนี้ว่า Hash Collision
ถามว่า อาการนี้เราทำให้มันหายไปเลยได้มั้ย คำตอบคือ ไม่ได้ แต่ !!! เราหลีกเลี่ยง หรือทำให้โอกาสที่มันจะเกิดน้อยลงได้ ด้วยการเพิ่มความยาวของค่า Hash เข้าไป เช่น Hashing Function โบราณ ๆ หน่อยอย่าง CRC-32 ใช้ความยาวทั้งหมด 32-bit พอมาเจอกับข้อมูลที่มีจำนวนเยอะ ๆ หน่อยก็ทำให้โอกาสที่จะเจออาการชนกันก็มีเยอะมาก จนตอนนี้ CRC-32 เป็น Hash Function ที่ไม่แนะนำให้ใช้แล้ว
Hash Function ใหม่ ๆ หลายตัวก็ถูกคิดค้นออกมา เพื่อทำให้การชนกันมัน และ การเดาย้อนกลับ ทำได้ยากขึ้นมาก ๆ เช่น bcrypt, scrypt และ Argon2
ลองนึกภาพตามว่า ถ้า User A ใช้รหัสผ่าน 123456 และ User B ใช้รหัสผ่านเดียวกัน ดังนั้นเมื่อเวลาเราจะเก็บรหัสผ่าน ค่าที่ Hash แล้วที่อยู่ในฐานข้อมูลของเราก็จะเหมือนกันเป๊ะเลย เพราะมันใช้วิธีเดียวกัน กับรหัสผ่านเดียวกันในการ Hash ยังไงก็ได้ค่าเดียวกันแน่นอน ซึ่งนั่นไม่ใช่เรื่องดีเลย
จากข้อมูลตรงนี้ ทำให้เจ้าของโปรแกรม หรือ คนที่เจาะเข้ามารู้เลยว่า คนกลุ่มนี้ใช้ Password เดียวกัน อาจจะเอาไป Brute Force ด้วยรหัสที่ง่าย ๆ ถ้าเข้าคนนี้ได้ คนที่เหลือที่มี Hashed Password เดียวกันก็ควรจะเข้าได้เช่นกัน นั่นก็ยังไม่ปลอดภัยในการใช้งานเท่าไหร่ ทำให้เราจำเป็นที่จะต้องเพิ่มอีกสิ่งนึงเข้าไป เป็นข้อความที่อาจจะเอามาต่อ หรือใส่ก่อนหน้า Password เพื่อให้ Hash ออกมาแล้วมันไม่เหมือนกัน เราเรียกข้อมูลที่มาเติมว่า Salt
ตัวอย่างถ้าเราเอาไปใช้งานกับระบบ Authentication ก็คือ เราอาจจะ Random Salt และเก็บไว้คู่กับแต่ละ User ไปเลย และพอเราจะเก็บรหัสผ่าน เราก็เอา Salt และ รหัสผ่านนั้นแหละ มาต่อกัน และค่อยเข้า Hash Function ที่เราเลือกเท่านี้ถึง User A จะมี Password ตัวเดียวกับ User B แต่ Hashed Value ก็จะไม่เหมือนกันนั่นเอง
เป็นคำถามที่ดีเลย สิ่งที่เหมือนกันคือ ทั้งคู่เป็น Function ที่ใช้ในการแปลงค่าเหมือนกัน แต่สิ่งที่ต่างกันคือ ความสัมพันธ์ของฉันและเธอ อิ้ววว ความสัมพันธ์ของ Function สิ อะถูกแล้ว ที่ Hash เป็น Function แบบ Many-to-one ที่ 1 ค่า Hash สามารถมาจากได้หลาย Password ทำให้ การที่เราจะแปลงค่า Hash กลับไปเป็น Password เดิม ทำได้ยาก หรือแทบทำไม่ได้เลย ทำให้ Password ของเรายังคงเป็นความลับอยู่
แต่กลับกัน Encrypt หรือการเข้ารหัส พวกนี้เราจะใช้ Function แบบ One-to-One หรือก็คือ 1 Password แปลงไปเป็นได้แค่ 1 Encrypted เท่านั้น ทั้งไปและกลับ นั่นทำให้เมื่อเรา เข้ารหัส ข้อมูล เรายังสามารถที่จะใช้ Key ในการ ถอดรหัส กลับมาเป็นข้อมูลเดิมได้
จากควาแตกต่าง ทำให้เราเห็นว่า ในการเก็บ Password เราไม่ควรอย่างยิ่งที่จะเก็บโดยการเข้ารหัส เพราะถ้าเกิด Key เกิดหลุดขึ้นมา ไม่ว่าจากเหตุผลใดก็ตาม Password ทั้งหมดที่ถูกเก็บ ก็จะสามารถเข้าถึงได้หมดเลย Hash เถอะนะคนดี
การเก็บ Password ในปัจจุบัน เรานิยมการทำ Hash กับ Password เพื่อทำให้ Password ที่ถูก Hash แล้ว ไม่สามารถย้อนกลับมาเป็น Password อันเดิมได้ (หรือ ทำได้ยากมาก กอไก่ ล้านตัว บน Hashing Function ใหม่ ๆ) ทำให้ไม่ว่าค่า Hash ที่เป็น Password จะหลุดออกไป คนที่แอบเอาไปก็ยังไม่รู้อยู่ดีว่า Password จริง ๆ มันคืออะไร แต่ Hash ก็ยังมีโอกาสที่จะชนกันได้ ทำให้เราจำเป็นที่จะต้องเติม Salt ซึ่งจะมาจากการสุ่มให้แต่ละ User ไม่เหมือนกัน ทำให้ เมื่อ User มีการใช้ Password ซำ้กันในระบบ ก็จะทำให้รู้ได้ยากขึ้นนั่นเอง ทำให้ปลอดภัยต่อผู้ใช้มากขึ้น และเราในฐานะผู้ใช้ก็วางใจได้มากขึ้นว่า Password ของเราไม่ได้เก็บกันโจ้ง ๆ
สำหรับคนที่เรียนคอมพิวเตอร์ มันยังมีลงลึกอีกนะว่า Hashing Function มันมีกี่ประเภทอะอะไรบ้าง และ เมื่อเกิดการชนกันจริง ๆ เราจะทำยังไงได้บ้าง เช่น Chaining, Probing และ Double Hashing หรือคำถามที่ว่า ทำไม bcrypt ถึงค่อนข้างทนต่อการทำ Brute Force (เพราะมันใช้หลาย CPU Cycle เยอะอยู่ ในการ Hash)
หลังจากเมื่อหลายอาทิตย์ก่อน Apple ออก Mac รัว ๆ ตั้งแต่ Mac Mini, iMac และ Macbook Pro ที่ใช้ M4 กันไปแล้ว มีหลายคนถามเราเข้ามาว่า เราควรจะเลือก M4 ตัวไหนดีถึงจะเหมาะกับเรา...
จากตอนก่อน เราเล่าเรื่องการ Host Website จากบ้านของเราอย่างปลอดภัยด้วย Cloudflare Tunnel ไปแล้ว แต่ Product ด้าน Zero-Trust ของนางยังไม่หมด วันนี้เราจะมาเล่าอีกหนึ่งขาที่จะช่วยปกป้อง Infrastructure และ Application ต่าง ๆ ของเราด้วย Cloudflare Access กัน...
ทุกคนเคยได้ยินคำว่า Mainframe Computer กันมั้ย เคยสงสัยกันมั้ยว่า มันต่างจากเครื่องคอมพิวเตอร์ที่เราใช้งานกันทั่ว ๆ ไปอย่างไรละ และ Mainframe ยังจำเป็นอยู่มั้ย มันได้ตายจากโลกนี้ไปหรือยัง วันนี้เรามาหาคำตอบไปด้วยกันเลย...
เคยมั้ยเวลา Deploy โปรแกรมสักตัว เราจะต้องมานั่ง Provision Infrastructure ไหนจะ VM และ Settings อื่น ๆ อีกมากมาย มันจะดีกว่ามั้ยถ้าเรามีเครื่องมือบางอย่างที่จะ Automate งานที่น่าเบื่อเหล่านี้ออกไป และลดความผิดพลาดที่อาจจะเกิดขึ้น วันนี้เราจะพาทุกคนมาทำความรู้จักกับ Infrastructure as Code กัน...