หมวดหมู่: PHP

PHP: การใช้ password_hash() และ password_verify() สำหรับระบบรหัสผ่านPHP: การใช้ password_hash() และ password_verify() สำหรับระบบรหัสผ่าน

ในโลกของการพัฒนาเว็บแอปพลิเคชัน การจัดการรหัสผ่านของผู้ใช้งานถือเป็นหัวใจหลักของความปลอดภัย (Security) อย่างยิ่ง ในอดีต นักพัฒนามักใช้ฟังก์ชันการเข้ารหัสแบบมาตรฐาน เช่น MD5, SHA-1 หรือ SHA-256 เพื่อ “แฮช” (Hash) รหัสผ่าน แต่แนวทางนี้ถูกพิสูจน์แล้วว่าไม่เพียงพอต่อการป้องกันในปัจจุบัน เนื่องจากฟังก์ชันเหล่านี้เป็น One-way function ที่เมื่อทราบค่า Hash แล้ว การถอดรหัสกลับไปหา Password ต้นฉบับนั้นทำได้ง่ายด้วยเทคนิค Brute Force หรือ Rainbow Table

PHP ได้พัฒนาชุดฟังก์ชันเฉพาะทางขึ้นมาเพื่อแก้ไขปัญหานี้ นั่นคือ password_hash() และ password_verify() ฟังก์ชันเหล่านี้ไม่ได้เพียงแค่ “แฮช” ข้อมูลเท่านั้น แต่ยังรวมเอาหลักการสำคัญด้านความปลอดภัยที่เรียกว่า Salt (ค่าสุ่มที่ไม่ซ้ำกัน) เข้าไปในกระบวนการโดยอัตโนมัติ และที่สำคัญที่สุดคือใช้กลไก Adaptive Hashing ซึ่งหมายถึงการทำให้กระบวนการแฮชมีความ “หน่วง” (Computational Cost) เพื่อให้แม้ผู้โจมตีจะสามารถคำนวณ Hash ได้จำนวนมาก ก็ต้องใช้เวลาและทรัพยากรสูงมากจนไม่คุ้มค่า


หลักการทำงานโดยสรุป:

  • password_hash(): ใช้สำหรับ “สร้าง” (Generate) Hash ของรหัสผ่าน โดยรับ Password ต้นฉบับและส่งออกเป็นสตริง Hash ที่มีข้อมูล Meta Data (เช่น Algorithm, Cost Factor และ Salt) ฝังอยู่ด้วย
  • password_verify(): ใช้สำหรับ “ตรวจสอบ” (Verify) ว่ารหัสผ่านที่ผู้ใช้ป้อนเข้ามานั้นตรงกับ Hash ที่ถูกจัดเก็บไว้ในฐานข้อมูลหรือไม่ โดยฟังก์ชันนี้จะดึงค่า Meta Data จาก Hash เก่ามาใช้ในการคำนวณ และเปรียบเทียบผลลัพธ์โดยไม่เปิดเผย Password ต้นฉบับ

ตัวอย่างการใช้งานและรูปแบบโค้ด (Code Examples)

เราจะจำลองกระบวนการ 2 ขั้นตอนหลัก คือ การลงทะเบียนผู้ใช้ (Hashing) และการเข้าสู่ระบบ (Verification)

<?php

// -------------------------------------------
// ส่วนที่ 1: การลงทะเบียนผู้ใช้ (Hashing)
// -------------------------------------------

/**
 * ฟังก์ชันสำหรับสร้าง Hash ของรหัสผ่าน
 * @param string $password รหัสผ่านต้นฉบับ
 * @return string Hash ที่เข้ารหัสแล้ว
 */
function hash_password(string $password): string {
    // PASSWORD_DEFAULT จะเลือก Algorithm ที่ปลอดภัยที่สุดตาม PHP Version (เช่น Bcrypt หรือ Argon2)
    // การระบุ cost เป็นการเพิ่มความยากในการคำนวณ
    $options = [
        'cost' => 12 // ค่า Cost เริ่มต้นที่ปลอดภัย
    ];

    $hash = password_hash($password, PASSWORD_DEFAULT, $options);

    if ($hash === false) {
        throw new \RuntimeException("Error: ไม่สามารถสร้าง Hash ได้ กรุณาลองใหม่อีกครั้ง");
    }
    return $hash;
}

// สมมติว่าผู้ใช้ป้อนรหัสผ่าน "MySecretPass123"
$plain_password = "MySecretPass123";
$stored_hash = hash_password($plain_password);

echo "========================================\n";
echo "รหัสผ่านต้นฉบับ: " . htmlspecialchars($plain_password) . "\n";
echo "Hash ที่จัดเก็บในฐานข้อมูล (Stored Hash):\n" . $stored_hash . "\n";
echo "----------------------------------------\n\n";


// -------------------------------------------
// ส่วนที่ 2: การเข้าสู่ระบบ (Verification)
// -------------------------------------------

/**
 * ฟังก์ชันสำหรับตรวจสอบรหัสผ่านเมื่อผู้ใช้ล็อกอิน
 * @param string $plain_password รหัสผ่านที่ผู้ใช้ป้อนเข้ามา
 * @param string $stored_hash Hash ที่ถูกดึงมาจากฐานข้อมูล
 * @return bool True หากรหัสผ่านถูกต้อง, False หากไม่ถูกต้อง
 */
function verify_password(string $plain_password, string $stored_hash): bool {
    // password_verify จะทำการแยกค่า Salt และ Algorithm จาก $stored_hash โดยอัตโนมัติ
    $is_valid = password_verify($plain_password, $stored_hash);

    if ($is_valid) {
        echo "✅ การตรวจสอบสำเร็จ: รหัสผ่านถูกต้อง!";
    } else {
        echo "❌ การตรวจสอบล้มเหลว: รหัสผ่านไม่ถูกต้อง";
    }
    return $is_valid;
}

// 1. ทดสอบด้วยรหัสผ่านที่ถูกต้อง
$login_attempt_success = verify_password("MySecretPass123", $stored_hash);
echo "\n\n[Test Success]: " . ($login_attempt_success ? 'SUCCESS' : 'FAIL') . "\n";

// 2. ทดสอบด้วยรหัสผ่านที่ผิดพลาด
$login_attempt_fail = verify_password("WrongPassword!", $stored_hash);
echo "\n[Test Failure]: " . ($login_attempt_fail ? 'SUCCESS' : 'FAIL');

?>

ข้อควรระวัง Security และ Best Practices

  • คำนึงถึงความปลอดภัย (Security): ห้ามใช้ฟังก์ชันแฮชแบบเก่าเด็ดขาด (MD5, SHA) ควรใช้ password_hash() เสมอ เนื่องจากมันจัดการ Salt และ Algorithm ที่เหมาะสมให้โดยอัตโนมัติ นอกจากนี้ ในการใช้งานจริง ต้องมั่นใจว่ารหัสผ่านที่ผู้ใช้ป้อนเข้ามานั้นเป็นค่า String ที่สะอาดและไม่มีช่องโหว่จากการ Injection (ควรมีการ Validate Input เสมอ)
  • Performance (Cost Factor): พารามิเตอร์ cost ใน password_hash() คือตัวกำหนดความยากในการคำนวณ การเพิ่มค่า Cost จะทำให้ระบบปลอดภัยยิ่งขึ้น แต่ก็จะใช้เวลา CPU มากขึ้นด้วย ดังนั้น ควรหาจุดสมดุลระหว่างความปลอดภัยและประสิทธิภาพของเซิร์ฟเวอร์ (โดยทั่วไป ค่า 10-12 ถือว่าเหมาะสมสำหรับปัจจุบัน)
  • Error Handling: ต้องมีการตรวจสอบผลลัพธ์จากทั้งสองฟังก์ชันเสมอ โดยเฉพาะการตรวจสอบว่าค่าที่ส่งกลับมาเป็น false หรือไม่ เพื่อป้องกันการทำงานต่อด้วย Hash ที่ผิดพลาด และควรใช้ Try-Catch Block ในส่วนของการจัดการฐานข้อมูลเพื่อความสมบูรณ์ของโค้ด

สรุปและการนำไปประยุกต์ใช้งาน

การใช้ password_hash() และ password_verify() ไม่ใช่แค่ทางเลือก แต่เป็นมาตรฐานความปลอดภัยที่จำเป็น (Mandatory Standard) สำหรับทุกแอปพลิเคชัน PHP ที่มีการจัดการรหัสผ่านของผู้ใช้งาน การทำเช่นนี้ช่วยให้ระบบของคุณสามารถป้องกันผู้โจมตีจากการเข้าถึงข้อมูลรหัสผ่านแม้ว่าฐานข้อมูลจะถูกขโมยไปก็ตาม


สรุปขั้นตอนการทำงานใน Production:

  • Registration: รับ Password -> เรียก password_hash() -> เก็บ Hash ที่ได้ลงในคอลัมน์รหัสผ่านของฐานข้อมูล
  • Login: ดึง Hash จาก DB -> รับ Password ที่ผู้ใช้ป้อน -> เรียก password_verify() เพื่อเปรียบเทียบ -> ถ้าเป็นจริง (True) ให้ถือว่าล็อกอินสำเร็จ

การทำความเข้าใจและนำหลักการเหล่านี้ไปใช้อย่างถูกต้อง จะช่วยยกระดับความปลอดภัยของระบบ PHP ของคุณให้ทัดเทียมกับมาตรฐานระดับสากลได้อย่างสมบูรณ์


อ่านเพิ่มเติม