Security
CSRF หรือ Cross-Site Request Forgery เนี่ย สมัยผมทำร้านเน็ต อาจจะไม่ฮิตเท่าสมัยนี้ แต่บอกเลยว่าอันตรายไม่แพ้กัน เพราะมันเป็นการหลอกให้เหยื่อ (ที่ล็อกอินอยู่) ทำอะไรบางอย่างที่เราต้องการ โดยที่เหยื่อไม่รู้ตัว
ลองนึกภาพนะ สมมติเหยื่อล็อกอินเข้าเว็บธนาคารอยู่ แล้วดันไปคลิกลิงก์ประหลาดที่เราส่งไป ลิงก์นั้นอาจจะสั่งโอนเงินออกจากบัญชีเหยื่อไปเข้าบัญชีเราก็ได้! น่ากลัวใช่มั้ยล่ะ
ทำไมมันถึงสำคัญ? ก็เพราะมันกระทบกับความน่าเชื่อถือของเว็บเราไง ถ้าลูกค้าโดน CSRF โจมตี แล้วเงินหาย ใครจะกล้าใช้เว็บเราอีก? แถมยังโดนฟ้องร้องได้อีกต่างหาก
<img src="http://bank.com/transfer?to=คนร้าย&amount=1000000">เห็นมั้ยว่ามันง่ายขนาดไหน? ที่สำคัญคือ เว็บธนาคาร "เชื่อ" ว่าเหยื่อเป็นคนสั่งเอง เพราะมี Cookie ยืนยันตัวตนอยู่
หลายคนสับสนระหว่าง CSRF กับ XSS (Cross-Site Scripting) ผมจะสรุปให้เข้าใจง่ายๆ นะ
XSS คือการฉีดโค้ด (ส่วนใหญ่เป็น JavaScript) เข้าไปในเว็บ เพื่อขโมยข้อมูล หรือทำอะไรอย่างอื่น ส่วน CSRF คือการ "หลอก" ให้เหยื่อทำอะไรบางอย่างที่เราต้องการ
เปรียบเทียบง่ายๆ XSS เหมือน "วางยา" ในเว็บ ส่วน CSRF เหมือน "สะกดจิต" ให้เหยื่อทำตามที่เราสั่ง
| คุณสมบัติ | CSRF | XSS |
|---|---|---|
| เป้าหมาย | หลอกให้เหยื่อ execute action | ฉีดโค้ดเข้าไปในเว็บ |
| วิธีโจมตี | ส่งลิงก์/ฟอร์มอันตราย | แทรก script ใน input, comment, etc. |
| ผลกระทบ | เปลี่ยนแปลงข้อมูล, โอนเงิน, etc. | ขโมย Cookie, redirect, deface |
| ป้องกัน | CSRF token, SameSite cookie | Sanitize input, escape output |
เข้าใจแล้วใช่มั้ย? ทั้งสองอย่างอันตรายพอกัน ต้องป้องกันให้ดี
SiamCafe Blogมาถึงส่วนสำคัญที่สุด: เราจะป้องกัน CSRF ได้ยังไง? ผมมีวิธีง่ายๆ 3 ข้อมาแนะนำ
วิธีนี้เป็นวิธีที่นิยมที่สุด และได้ผลดีที่สุด หลักการคือ เราจะสร้าง "Token" ที่ไม่ซ้ำกัน ในแต่ละ session และใส่ Token นี้ไว้ในฟอร์มที่เราต้องการป้องกัน
เวลา Server ได้รับ request ก็จะตรวจสอบว่า Token ที่ส่งมา ตรงกับ Token ใน session หรือเปล่า ถ้าไม่ตรง ก็แสดงว่าเป็นการโจมตี CSRF
Code ตัวอย่าง (PHP):
<?php
session_start();
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['csrf_token'];
?>
<form action="process.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token; ?>">
<input type="text" name="name">
<button type="submit">Submit</button>
</form>
ในไฟล์ process.php เราก็ตรวจสอบ Token:
<?php
session_start();
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF detected!');
}
// Process the form data here
echo "Form submitted successfully!";
?>
SameSite Cookie เป็น attribute ของ Cookie ที่กำหนดว่า Cookie จะถูกส่งไปพร้อมกับ request จาก domain อื่นได้หรือไม่
มี 3 ค่าให้เลือก:
Secure attribute) ไม่แนะนำให้ใช้การตั้งค่า SameSite Cookie (PHP):
<?php
setcookie('session_id', '12345', ['samesite' => 'Strict', 'secure' => true]);
?>
วิธีนี้คล้ายกับ CSRF Token แต่เราจะเก็บ Token ไว้ใน Cookie แทนที่จะเก็บใน Session
หลักการคือ สร้าง Token, ตั้งค่า Token ใน Cookie และใส่ Token ใน Form Field เวลา submit เราจะตรวจสอบว่า Token ใน Cookie ตรงกับ Token ใน Form Field หรือไม่
ข้อควรระวัง: วิธีนี้ต้องระวังเรื่อง XSS เพราะถ้าโดน XSS โจมตี คนร้ายสามารถอ่านค่า Cookie ได้
SiamCafe Blogดูวิดีโอเพิ่มเติมเกี่ยวกับCSRF Attack คืออะไร ป้องกันยัง:
หวังว่าบทความนี้จะเป็นประโยชน์นะครับ ถ้ามีคำถามอะไรเพิ่มเติม ถามมาได้เลย!
สมัยผมทำร้านเน็ต STP นี่แหละตัวหลักเลย ง่ายสุด เข้าใจง่ายสุด หลักการคือ ทุกฟอร์มที่เราสร้าง ให้ generate token สุ่มขึ้นมา 1 ค่า เก็บไว้ใน session แล้วส่ง token นี้ไปพร้อมฟอร์ม ตอนรับฟอร์มมา ก็เช็คว่า token ที่ส่งมา ตรงกับ token ใน session ไหม ถ้าไม่ตรง แสดงว่าไม่ใช่ฟอร์มเรา
<form method="POST" action="/transfer">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<input type="text" name="amount">
<button type="submit">โอนเงิน</button>
</form>
Code ตัวอย่างข้างบน คือเราสร้าง input hidden ชื่อ csrf_token แล้วใส่ค่า token ที่ generate ไว้ใน session เข้าไป เวลา submit ฟอร์ม token นี้ก็จะถูกส่งไปด้วย
อันนี้ก็คล้ายๆ STP แต่แทนที่จะเก็บ token ใน session เราจะเก็บใน cookie แทน แล้วก็ส่ง token เดียวกันนั้นไปใน header ของ request ด้วย ตอน server ได้รับ request ก็เช็คว่า token ใน cookie กับ token ใน header ตรงกันไหม ถ้าไม่ตรงก็ block
วิธีนี้ข้อดีคือไม่ต้องพึ่ง session แต่ข้อเสียคือต้องจัดการเรื่อง cookie ให้ดีๆ อย่าให้โดน hijack ได้ และต้องระวังเรื่อง subdomain ด้วย
อันนี้เป็น option ของ cookie ที่ browser สมัยใหม่รองรับแล้ว ง่ายๆ เลยคือเรากำหนดว่า cookie นี้จะถูกส่งไปกับ request เฉพาะตอนที่ request นั้นมาจาก domain เดียวกันเท่านั้น ป้องกัน request ข้าม domain ได้ระดับนึง
Set-Cookie: csrf_token=random_value; SameSite=Strict;
SameSite=Strict คือ cookie จะถูกส่งไปเฉพาะ request ที่มาจาก domain เดียวกันเท่านั้น ส่วน SameSite=Lax จะอนุญาตให้ส่งไปกับ GET request ที่มาจาก domain อื่นได้
วิธีนี้ง่ายๆ เลย คือเราเช็คว่า request ที่เข้ามา มี Referer header เป็น domain ของเราหรือเปล่า ถ้าไม่ใช่ก็ block ไปเลย แต่... Referer header มันเชื่อถือไม่ได้ 100% นะ บาง browser ก็ไม่ส่ง บางที user ก็ปิดไว้ วิธีนี้ใช้เป็นแค่ตัวช่วยเสริมละกัน อย่าไปหวังพึ่งมาก
CSRF คือการหลอกให้ user ทำอะไรบางอย่างบนเว็บที่เรา login อยู่ โดยที่เราไม่ได้ตั้งใจ ส่วน XSS คือการแทรก script อันตรายเข้าไปในเว็บให้คนอื่นโดน ตอนผมทำร้านเน็ตสมัยก่อน XSS นี่ตัวดีเลย โดน inject script ให้ redirect ไปเว็บโป๊ประจำ
iCafeForex ก็เคยเจอ XSS เหมือนกัน แต่เป็นอีกรูปแบบหนึ่ง
ก็เพราะว่าถ้าไม่ป้องกัน คนร้ายมันจะแอบโอนเงินออกจากบัญชีเราได้ไง! หรือเปลี่ยนข้อมูลส่วนตัวเรา หรือทำอะไรก็ได้ที่เราทำได้บนเว็บนั้นแหละ แค่เราไม่รู้ตัวเท่านั้นเอง
Framework พวกนี้ส่วนใหญ่จะมีระบบป้องกัน CSRF ให้เราอยู่แล้ว แค่เราเปิดใช้งานมัน แล้วก็ใส่ token ลงไปในฟอร์มตามที่ framework บอก แค่นั้นเอง ไม่ต้องเขียนเองให้ปวดหัว
การป้องกัน CSRF เป็นแค่ส่วนหนึ่งของการรักษาความปลอดภัย ยังมีช่องโหว่อื่นๆ อีกเยอะแยะที่ต้องระวัง เช่น XSS, SQL Injection, Brute Force Attack ดังนั้นต้องดูแลเว็บเราให้ดีๆ รอบด้าน
CSRF Attack เป็นภัยคุกคามที่น่ากลัว แต่ก็ป้องกันได้ไม่ยาก แค่เราเข้าใจหลักการทำงานของมัน แล้วก็เลือกใช้ Best Practices ที่เหมาะสมกับเว็บของเรา ที่สำคัญคือต้องอัพเดทความรู้เรื่อง security อยู่เสมอ เพราะ Hacker มันก็พัฒนาวิธีการโจมตีอยู่ตลอดเวลาเหมือนกัน
ลองอ่านบทความอื่นๆ เพิ่มเติมได้ที่ SiamCafe Blog นะครับ