Argon2 ได้รับการคัดเลือกให้เป็นผู้ชนะในงาน Password Hashing Competition (PHC) ในปี 2015 ด้วยการออกแบบที่เน้นการใช้ทรัพยากรทั้ง CPU, หน่วยความจำ (RAM) และสามารถปรับแต่งให้ใช้หลายเธรดได้ ทำให้การโจมตีแบบ Mass-parallelization (โจมตีพร้อมกันจำนวนมาก) ทำได้ยากและมีค่าใช้จ่ายสูง
บทความนี้จะแนะนำวิธีการใช้งาน Argon2 ใน Python เพื่อ Hash และตรวจสอบรหัสผ่านของคุณอย่างปลอดภัย
ทำไมต้อง Argon2
Argon2 ถูกออกแบบมาเพื่อแก้ไขจุดอ่อนที่อาจเกิดขึ้นในอัลกอริทึม Hash รหัสผ่านรุ่นก่อนๆ โดยเฉพาะอย่างยิ่งความสามารถในการต้านทานการโจมตีด้วยฮาร์ดแวร์เฉพาะทาง (ASIC) และ GPU:
- Memory-hard: Argon2 ต้องการหน่วยความจำจำนวนมากในการคำนวณ Hash ทำให้การสร้างฮาร์ดแวร์เฉพาะทาง (ASIC) เพื่อโจมตีนั้นมีต้นทุนสูงมาก
- Time-hard: ต้องใช้เวลาในการคำนวณนาน (คล้ายกับ Bcrypt และ Scrypt) ซึ่งทำให้การโจมตีแบบ Brute-force ช้าลงอย่างมาก
- Parallelism-hard: สามารถปรับแต่งให้ใช้หลาย CPU core/thread ได้อย่างมีประสิทธิภาพ ทำให้การเร่งความเร็วในการโจมตีแบบขนาน (Parallelization) เป็นไปได้ยากและแพง
- Flexible Parameters: Argon2 มีพารามิเตอร์ที่สามารถปรับแต่งได้ละเอียดกว่า ทำให้สามารถปรับให้เข้ากับความต้องการด้านความปลอดภัยและทรัพยากรของระบบได้อย่างเหมาะสม
Argon2 มีหลาย "flavors" (รุ่นย่อย) ที่ใช้บ่อยคือ Argon2id ซึ่งรวมเอาข้อดีของ Argon2i (เน้นการเข้าถึงหน่วยความจำแบบอิสระเพื่อป้องกันการโจมตีจาก GPU) และ Argon2d (เน้นการเข้าถึงหน่วยความจำแบบขึ้นกับข้อมูลเพื่อป้องกันการโจมตีจาก Side-channel) เข้าไว้ด้วยกัน ทำให้เป็นตัวเลือกที่แนะนำสำหรับแอปพลิเคชันส่วนใหญ่
เริ่มต้นใช้งาน Argon2 ใน Python
ในการใช้ Argon2 ใน Python เราจะใช้ไลบรารีที่ชื่อว่า argon2-cffi ซึ่งเป็น Python binding สำหรับไลบรารี Argon2 ที่เขียนด้วยภาษา C (cffi ย่อมาจาก C Foreign Function Interface)
คุณสามารถติดตั้งได้ง่ายๆ ด้วย pip:
โค้ด: เลือกทั้งหมด
pip install argon2-cffi
ในการ Hash รหัสผ่านด้วย Argon2 คุณจะต้องกำหนดพารามิเตอร์หลักสามตัว ได้แก่:
- memory_cost (m): จำนวนหน่วยความจำที่จะใช้ในการคำนวณ (ในหน่วย KiB) ยิ่งมากยิ่งปลอดภัยและต้านทาน GPU ได้ดีขึ้น
- time_cost (t): จำนวนครั้งที่อัลกอริทึมจะวนซ้ำ ยิ่งมากยิ่งปลอดภัยแต่ใช้เวลานานขึ้น
- parallelism (p): จำนวนเธรด/คอร์ที่จะใช้ในการคำนวณ (แนะนำให้ใช้ 1 หรือเท่ากับจำนวนคอร์ของ CPU หากต้องการประสิทธิภาพสูงสุดในการ Hash)
argon2-cffi จะจัดการเรื่องการสร้าง Salt และการเข้ารหัสพารามิเตอร์เหล่านี้ลงใน Hash Output ให้โดยอัตโนมัติ
โค้ด: เลือกทั้งหมด
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
# สร้าง instance ของ PasswordHasher
# สามารถกำหนดค่า memory_cost, time_cost, parallelism ได้
# ค่าเริ่มต้นที่แนะนำมักจะเพียงพอสำหรับแอปพลิเคชันส่วนใหญ่
# ตัวอย่าง: ph = PasswordHasher(memory_cost=65536, time_cost=4, parallelism=2)
# ค่าเริ่มต้นของ argon2-cffi อาจจะประมาณ memory_cost=1024*128 (128 MiB), time_cost=2, parallelism=8
# สำหรับการใช้งานจริง ควรทดสอบหาค่าที่เหมาะสมกับ server resource ของคุณ
ph = PasswordHasher()
def hash_password_argon2(password):
return ph.hash(password)
# --- ตัวอย่างการใช้งาน ---
plain_password = "MyUltraSecurePassword!"
hashed_pwd_argon2 = hash_password_argon2(plain_password)
print(f"Plain Password: {plain_password}")
print(f"Hashed Password (Argon2): {hashed_pwd_argon2}")
- PasswordHasher(): สร้างอ็อบเจกต์ PasswordHasher คุณสามารถส่งพารามิเตอร์ memory_cost, time_cost, และ parallelism เข้าไปใน constructor เพื่อปรับแต่งความแรงของ Hash ได้
- ph.hash(password): ฟังก์ชันนี้จะรับรหัสผ่านที่เป็น String (ไลบรารีนี้จัดการการ encode เป็น bytes ให้เอง) และคืนค่า Hash ที่สมบูรณ์แบบที่เป็น String ซึ่งประกอบด้วยข้อมูลเกี่ยวกับเวอร์ชันของ Argon2, พารามิเตอร์ที่ใช้, Salt และ Hash Value จริงๆ
ผลลัพธ์
โค้ด: เลือกทั้งหมด
Plain Password: MyUltraSecurePassword!
Hashed Password (Argon2): $argon2id$v=19$m=65536,t=3,p=4$2dxEnSLjyh4vCtfjKqwAwg$SUlN/hPPmL+8UJSIBXjpB/BSGzOaIxl4zgH1flC/YzM
เมื่อผู้ใช้พยายามเข้าสู่ระบบ คุณจะต้องนำรหัสผ่านที่ผู้ใช้ป้อนเข้ามาไปเปรียบเทียบกับ Hash ที่เก็บอยู่ในฐานข้อมูล ซึ่ง argon2-cffi ก็มีฟังก์ชันสำหรับทำสิ่งนี้โดยเฉพาะ
โค้ด: เลือกทั้งหมด
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
# สามารถกำหนดค่า memory_cost, time_cost, parallelism ได้
# ค่าเริ่มต้นที่แนะนำมักจะเพียงพอสำหรับแอปพลิเคชันส่วนใหญ่
# ตัวอย่าง: ph = PasswordHasher(memory_cost=65536, time_cost=4, parallelism=2)
# ค่าเริ่มต้นของ argon2-cffi อาจจะประมาณ memory_cost=1024*128 (128 MiB), time_cost=2, parallelism=8
# สำหรับการใช้งานจริง ควรทดสอบหาค่าที่เหมาะสมกับ server resource ของคุณ
# สร้าง instance ของ PasswordHasher
ph = PasswordHasher()
def verify_password_argon2(plain_password, hashed_password_from_db):
try:
ph.verify(hashed_password_from_db, plain_password)
return True
except VerifyMismatchError:
# เกิดเมื่อรหัสผ่านไม่ตรงกัน
return False
except Exception as e:
# กรณีอื่นๆ เช่น hash string เสียหาย
print(f"An error occurred during verification: {e}")
return False
def hash_password_argon2(password):
return ph.hash(password)
# --- ตัวอย่างการใช้งาน ---
plain_password = "MyUltraSecurePassword!"
hashed_pwd_argon2 = hash_password_argon2(plain_password)
print(f"Plain Password: {plain_password}")
if verify_password_argon2(plain_password, hashed_pwd_argon2):
print(f"'{plain_password}' - Login successful!")
else:
print(f"'{plain_password}' - Login failed!")
- ph.verify(hashed_password_from_db, plain_password): ฟังก์ชันนี้จะรับ Hash ที่เก็บไว้ในฐานข้อมูล (ซึ่งมี Salt และพารามิเตอร์ทั้งหมดฝังอยู่) และรหัสผ่าน Plain Text ที่ผู้ใช้ป้อนเข้ามา หากรหัสผ่านตรงกัน ฟังก์ชันจะทำงานได้สำเร็จ (ไม่เกิด Exception) หากไม่ตรงกัน จะเกิด VerifyMismatchError
- Error Handling: การใช้ try-except VerifyMismatchError เป็นสิ่งสำคัญมากในการจัดการผลลัพธ์ของการตรวจสอบรหัสผ่านอย่างถูกต้อง
ผลลัพธ์ สรุป
Argon2 เป็นอัลกอริทึมการ Hash รหัสผ่านที่ทันสมัยและแข็งแกร่งที่สุดในปัจจุบัน ด้วยคุณสมบัติ Memory-hard และ Parallelism-hard ทำให้มันเป็นตัวเลือกที่ยอดเยี่ยมในการปกป้องข้อมูลรหัสผ่านของผู้ใช้จากภัยคุกคามที่พัฒนาไปอย่างรวดเร็ว
การนำ argon2-cffi ไปใช้งานในแอปพลิเคชัน Python ของคุณเป็นขั้นตอนที่สำคัญในการยกระดับความปลอดภัย ซึ่งเป็นการลงทุนที่คุ้มค่าเพื่อรักษาความน่าเชื่อถือและความไว้วางใจของผู้ใช้ของคุณครับ
อ้างอิง
- https://www.mindphp.com/developer/80-ph ... ction.html
- viewtopic.php?p=441753
- viewtopic.php?t=116518
- https://www.mindphp.com/บทเรียนออนไลน์/ ... iable.html
- https://github.com/hynek/argon2-cffi