Skip to main content

เนื้อหาภาษาไทย / Thai content

System guide Content reviewed

CSRF Protection: หลักการทำงานและการป้องกัน

บทความนี้อธิบายถึงภัยคุกคาม Cross-Site Request Forgery (CSRF) ซึ่งเป็นการโจมตีที่ผู้ไม่หวังดีหลอกให้เหยื่อที่ล็อกอินอยู่ทำการ request โดยไม่รู้ตัว โดยอาศัยการที...

CSRF Protection: หลักการทำงานและการป้องกัน

ในบทที่แล้ว เราได้เรียนรู้เกี่ยวกับการป้องกัน SQL Injection ด้วย Prepared Statements คราวนี้เราจะมาดูภัยคุกคามอีกรูปแบบหนึ่งที่เรียกว่า Cross-Site Request Forgery (CSRF) และวิธีการป้องกัน

ความหมายของ CSRF

CSRF (Cross-Site Request Forgery) คือการโจมตีที่ผู้ไม่หวังดีใช้ให้เหยื่อ (ผู้ใช้ที่ล็อกอินอยู่) ทำการ request ที่ไม่ต้องการโดยที่เหยื่อไม่รู้ตัว ตัวอย่างเช่น ผู้โจมตีอาจหลอกให้เหยื่อเปลี่ยนรหัสผ่านหรือโอนเงินโดยที่เหยื่อไม่ได้ตั้งใจ

ลองจินตนาการ: คุณล็อกอินเข้าสู่เว็บไซต์ธนาคารของคุณ และกำลังอ่านอีเมลในแท็บอื่น อีเมลนั้นมีลิงก์ที่ดูเหมือนจะนำไปสู่โปรโมชั่น แต่จริงๆ แล้วมันเป็นลิงก์ที่ส่งคำสั่งโอนเงินออกจากบัญชีของคุณไปยังบัญชีของผู้โจมตี เนื่องจากคุณล็อกอินอยู่กับเว็บไซต์ธนาคาร และเบราว์เซอร์จะส่ง cookies ของเว็บไซต์ธนาคารไปพร้อมกับ request นั้น ธนาคารจึงเข้าใจผิดว่าคุณเป็นคนสั่งโอนเงินเอง!

CSRF อาศัยความจริงที่ว่าเบราว์เซอร์จะส่ง cookies ที่เกี่ยวข้องกับเว็บไซต์ไปพร้อมกับทุก request โดยอัตโนมัติ ไม่ว่า request นั้นจะมาจากที่ใดก็ตาม

หลักการทำงานของ CSRF

  1. ผู้ใช้ล็อกอินเข้าสู่เว็บไซต์ (เช่น ธนาคารออนไลน์)
  2. เว็บไซต์เก็บข้อมูลการล็อกอินของผู้ใช้ไว้ใน cookie
  3. ผู้ใช้เข้าชมเว็บไซต์ที่เป็นอันตราย (หรือได้รับอีเมลที่มีลิงก์อันตราย)
  4. เว็บไซต์อันตรายนั้นสร้าง request ไปยังเว็บไซต์ที่ผู้ใช้ล็อกอินอยู่ (เช่น ธนาคารออนไลน์) โดยใช้ข้อมูลที่อยู่ใน cookie ของผู้ใช้
  5. เว็บไซต์ที่ผู้ใช้ล็อกอินอยู่ ตรวจสอบ cookie และเชื่อว่า request นั้นมาจากผู้ใช้ที่ถูกต้อง
  6. เว็บไซต์ดำเนินการตาม request นั้น (เช่น โอนเงิน)

สรุปง่ายๆ: ผู้โจมตีหลอกให้เบราว์เซอร์ของผู้ใช้ส่งคำขอไปยังเว็บไซต์ที่ผู้ใช้ล็อกอินอยู่ โดยที่ผู้ใช้ไม่ได้ตั้งใจ

การป้องกัน CSRF

วิธีหลักในการป้องกัน CSRF คือการใช้ CSRF token

CSRF token คือค่าที่ไม่ซ้ำกัน (unique) และคาดเดาได้ยาก ซึ่งสร้างขึ้นสำหรับแต่ละ session หรือแต่ละ request ค่านี้จะถูกส่งไปพร้อมกับ request และเว็บไซต์จะตรวจสอบว่า token ที่ได้รับนั้นถูกต้องหรือไม่ หาก token ไม่ถูกต้อง แสดงว่า request นั้นอาจมาจากผู้โจมตี

ขั้นตอนการทำงานของการป้องกัน CSRF:

  1. เมื่อผู้ใช้เข้าชมหน้าเว็บที่มี form (เช่น form เปลี่ยนรหัสผ่าน) เว็บไซต์จะสร้าง CSRF token ใหม่
  2. เว็บไซต์จะเก็บ CSRF token ไว้ใน session ของผู้ใช้
  3. เว็บไซต์จะเพิ่ม CSRF token ลงใน form เป็น hidden field
  4. เมื่อผู้ใช้ส่ง form เว็บไซต์จะตรวจสอบว่า CSRF token ที่ส่งมาพร้อมกับ form ตรงกับ token ที่เก็บไว้ใน session หรือไม่
  5. หาก token ตรงกัน แสดงว่า request นั้นมาจากผู้ใช้ที่ถูกต้อง และเว็บไซต์จะดำเนินการตาม request นั้น
  6. หาก token ไม่ตรงกัน แสดงว่า request นั้นอาจมาจากผู้โจมตี และเว็บไซต์จะปฏิเสธ request นั้น

การสร้าง CSRF Token

CSRF token ควรเป็นค่าที่คาดเดาได้ยาก โดยทั่วไปจะใช้ฟังก์ชันที่สร้างเลขสุ่มที่ปลอดภัย เช่น `random_bytes()` หรือ `openssl_random_pseudo_bytes()`

<?php
session_start();

function generateCSRFToken() {
  return bin2hex(random_bytes(32)); // สร้าง token ขนาด 64 ตัวอักษร
}

if (!isset($_SESSION['csrf_token'])) {
  $_SESSION['csrf_token'] = generateCSRFToken();
}

$csrf_token = $_SESSION['csrf_token'];
?>

คำอธิบาย:

  • `session_start()`: เริ่มต้น session เพื่อเก็บ CSRF token
  • `generateCSRFToken()`: ฟังก์ชันที่สร้าง CSRF token โดยใช้ `random_bytes()` และแปลงเป็น hexadecimal
  • `if (!isset($_SESSION['csrf_token']))`: ตรวจสอบว่ามี CSRF token ใน session หรือยัง ถ้ายังไม่มี ให้สร้างใหม่
  • `$_SESSION['csrf_token'] = generateCSRFToken()`: เก็บ CSRF token ไว้ใน session
  • `$csrf_token = $_SESSION['csrf_token']`: ดึง CSRF token จาก session เพื่อนำไปใช้ใน form

การเพิ่ม CSRF Token ใน Form

เมื่อสร้าง CSRF token แล้ว ให้เพิ่ม token นั้นลงใน form เป็น hidden field

<form action="process_form.php" method="post">
  <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
  <label for="new_password">New Password:</label>
  <input type="password" id="new_password" name="new_password"><br><br>
  <button type="submit">Change Password</button>
</form>

คำอธิบาย:

  • `<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">`: เพิ่ม hidden field ชื่อ `csrf_token` พร้อมกับค่าของ CSRF token
  • `htmlspecialchars($csrf_token)`: ใช้ `htmlspecialchars()` เพื่อป้องกัน XSS (Cross-Site Scripting) โดยการแปลง character พิเศษให้เป็น HTML entities

การตรวจสอบ CSRF Token

เมื่อ form ถูกส่งมา ให้ตรวจสอบว่า CSRF token ที่ส่งมาพร้อมกับ form ตรงกับ token ที่เก็บไว้ใน session หรือไม่

<?php
session_start();

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  if (isset($_POST['csrf_token']) && isset($_SESSION['csrf_token'])) {
    if ($_POST['csrf_token'] === $_SESSION['csrf_token']) {
      // CSRF token ถูกต้อง ดำเนินการต่อ
      // ... ประมวลผลข้อมูล form ...
      echo "CSRF token ถูกต้อง! กำลังประมวลผลข้อมูล...";

      // หลังจากประมวลผลเสร็จสิ้น ควรลบ CSRF token เก่าและสร้างใหม่
      unset($_SESSION['csrf_token']);
      $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // สร้าง token ใหม่
    } else {
      // CSRF token ไม่ถูกต้อง
      echo "CSRF token ไม่ถูกต้อง! การดำเนินการถูกยกเลิก";
      exit;
    }
  } else {
    // ไม่มี CSRF token ใน POST request หรือ session
    echo "ไม่มี CSRF token! การดำเนินการถูกยกเลิก";
    exit;
  }
}
?>

คำอธิบาย:

  • `$_SERVER["REQUEST_METHOD"] == "POST"`: ตรวจสอบว่า request เป็น POST request
  • `isset($_POST['csrf_token']) && isset($_SESSION['csrf_token'])`: ตรวจสอบว่ามี CSRF token ใน POST request และ session
  • `$_POST['csrf_token'] === $_SESSION['csrf_token']`: เปรียบเทียบ CSRF token ที่ส่งมากับ token ที่เก็บไว้ใน session
  • หาก token ตรงกัน แสดงว่า request นั้นมาจากผู้ใช้ที่ถูกต้อง และสามารถประมวลผลข้อมูล form ได้
  • หาก token ไม่ตรงกัน แสดงว่า request นั้นอาจมาจากผู้โจมตี และควรหยุดการดำเนินการ
  • `unset($_SESSION['csrf_token'])`: ลบ CSRF token เก่าออกจาก session หลังจากประมวลผล form สำเร็จ
  • `$_SESSION['csrf_token'] = bin2hex(random_bytes(32))`: สร้าง CSRF token ใหม่สำหรับ request ถัดไป

ข้อควรจำ:

  • ทุกครั้งที่ประมวลผล form สำเร็จ ควรสร้าง CSRF token ใหม่
  • CSRF token ควรเป็นค่าที่ไม่ซ้ำกันและคาดเดาได้ยาก
  • ควรใช้ `htmlspecialchars()` เพื่อป้องกัน XSS เมื่อแสดง CSRF token ใน HTML

ในบทต่อไป เราจะมาดูวิธีการสร้าง form พร้อม CSRF token อย่างละเอียด

Office.in.th Custom Service

Want to build or adapt a system from this structure?

Office.in.th designs and develops enterprise web applications, API integrations, and public- and private-sector management systems, with long-term support.

See our software development service