Showing posts with label hash. Show all posts
Showing posts with label hash. Show all posts

Saturday, May 2, 2009

Oracle เก็บพาสเวิร์ดอย่างไร

Updated: 1/4/2007

ในการเก็บพาสเวิร์ดของ user ปกติ (ที่ไม่ใช่ user ที่ได้สิทธิ์ SYSDBA) Oracle ไม่ได้ใช้วิธีการเข้ารหัส-ถอดรหัส (Encrypted-Decrypted) ในการเก็บพาสเวิร์ดให้เป็นความลับเพราะการใช้วิธีนั้น ทำให้เกิดคำถามขึ้นมาว่า ถ้าอัลกอริทึมนั้น (อาจจะบวกกับคีย์ในการเข้ารหัส) คนที่รู้นำไปใช้ในทางที่ผิดเช่น ไปแฮ็คเอาพาสเวิร์ดของบุคคล (ที่ใช้ Oracle) ที่มีหน้าที่เกี่ยวกับการเงินในองค์กร หรือแฮ็คเอาพาสเวิร์ดของ database admin ของธนาคารเป็นต้น

จริงๆ แล้ว Oracle ไม่ได้เข้ารหัสพาสเวิร์ด แต่ใช้อัลกอริทึมที่เรียกว่า Hash แทนคือ ปกติถ้าคุณเข้ารหัส คุณจะได้ชุดของตัวอักษรที่ดูไม่รู้เรื่องออกมา พอจะใช้งานคุณก็ถอดรหัสออกดังนี้

การเข้ารหัส: 'MyPassword' ---- เข้ารหัส-----> 'AE045B73'
การถอดรหัส: 'AE045B73' -----ถอดรหัส -----> 'MyPassword'

แต่ลักษณะของ Hash อัลกอริทึมจะเป็นการเอาข้อมูลที่เป็น username และ password มาผ่าน Hash อัลกอริทึมที่จะเอาข้อมูลทั้งสองมา 'ยำ' (ขออนุญาตใช้คำนี้นะครับ) กันเพื่อแปลงให้ออกมาเป็นค่าๆ หนึ่ง (โดยปกติจะเป็นเลขฐานสิบหกจำนวนหลายๆหลักเช่น 32 หลักเป็นต้น) เวลาจะตรวจเช็คจะไม่มีการดึงข้อมูลที่เป็นค่าที่ 'ยำ' แล้วออกมา แต่จะเป็นการเอาค่าทั้งสองมา 'ยำ' กันอีกด้วยอัลกอริทึมเดิม แล้วไปเช็คกับค่าที่ยำกันไว้ก่อนหน้าว่าตรงกันหรือเปล่า

การเก็บรหัส: 'UserName' + 'Password' ---- hashed ----> 'A304Fe89604353fb'
การตรวจสอบรหัส: 'UserName' + 'Password' ---- hashed ----> 'A304Fe89604353fb'

ด้วยวิธีการนี้ค่าที่ใส่เข้าไปทั้งสองค่าจะเป็นคีย์ของมันเอง ไม่ต้องใช้คีย์อื่นใดอีก Hash อัลกอริทึมที่ใช้ก็เป็นอัลกอริทึมมาตรฐานที่ใช้กันทั่วๆไป เช่น MD5 หรือจะคิดขึ้นมาใหม่ก็ได้ ส่วนวิธีการที่ Oracle ใช้ในการเข้ารหัสพาสเวิร์ดของตัวเองเป็นดังนี้ครับ

function digest( p_username in varchar2, p_password in varchar2 )
return varchar2
is
begin
return ltrim( to_char( dbms_utility.get_hash_value( upper(p_username)||'/'||upper(p_password),1000000000, power(2,30) ),rpad( 'X',29,'X')||'X' ) );
end digest;

ฟังก์ชัน digest นี้จะนำรับเอาค่าพารามิเตอร์ username และพาสเวิร์ด เอามา Hash ให้เป็นค่าใดๆจากใน 1073741824 (หนึ่งพันเจ็ดสิบสามล้านเจ็ดแสนสี่หมื่นหนึ่งพันแปดร้อยยี่สิบสี่) ค่า บวกกับอีก 1000000000 (หนึ่งพันล้าน) เพื่อที่จะให้ค่าใหญ่ขึ้น แล้วแปลงให้เป็นเลขฐานสิบหก แล้วจึงนำไปเก็บในฟิลด์พาสเวร์ด คราวนี้ถ้า user ต้องการล็อกอินก็ใส่ username และพาสเวิร์ดเข้ามาแล้วผ่านฟังก์ชันเดียวกันนี้ ถ้าผลที่ได้ตรงกับค่าที่เก็บในฟิลด์พาสเวิร์ดก็ผ่าน ถ้าไม่ก็ไม่ผ่าน

- Hash อัลกอริทึมเป็นการแม็บกลุ่มของตัวอักษรใดๆ (String) ที่ไร้ค่าจำกัดและขอบเขตใดๆ (username+พาสเวิร์ด) กับค่าที่มีจำนวนจำกัด (เช่น 1073741824) ดังนั้นอาจจะเป็นไปได้ว่ามีค่าสองค่าใดๆซึ่งเป็น username+พาสเวิร์ด ที่หลังจากการ hash แล้วให้ค่าเดียวกัน แต่ก็มีความเป็นไปได้ยากโดยเฉพาะถ้าเรากำหนดให้ขนาดของเซ็ทของค่าที่เป็นผลของ hash มีขนาดใหญ่ขึ้น อย่างในกรณีของฟังก์ชัน Hash ข้างบนค่าที่จะเป็นไปได้มีถึง 1,073,741,824 ทีเดียว (ผมลองใช้ Hash อัลกอริทึม ที่เป็น MD5 จะได้ตัวเลขฐานสิบหกถึง 32 ตัว ซึ่งเซ็ทของค่าที่เป็นไปได้ก็คือ 16 ยกกำลัง 32 ซึ่งเป็นขนาดของเซ็ทที่มีขนาดใหญ่มาก)
- Hash ไม่มีอัลกอริทึมในการย้อนกลับ (ซึ่งก็ไม่จำเป็นที่จะต้องทำ) เหมือนกับการเข้ารหัส-ถอดรหัส สิ่งที่ต้องการคือเอาแค่ username และพาสเวิร์ดใส่เข้าไปเท่านั้น ดังนั้นจึงค่อนข้างปลอดภัย (เพราะมันไม่มีอัลกอริทึมในการย้อนกลับนั่นเอง)Hash อัลกอริทึมของ Oracle เองใน DBMS_UTILITY.GET_HASH_VALUE อาจจะมีการเปลี่ยนแปลงไปได้ในแต่ละเวอร์ชั่น ดังนั้นคุณอาจจะเลือกที่จะใช้อัลกอริทึมที่เป็นมาตรฐานอย่าง MD5 (ใน Oracle10g คือ dbms_crypto.HASH_MD5 ส่วน Oracle9i อาจจะต้องใช้ dbms_obfuscation_toolkit.MD5แทน) เป็นต้น