Cross-Site Request Forgery (CSRF)
CSRF หลอกให้เบราว์เซอร์ของเหยื่อส่ง request ที่มี side-effect ไปยังเว็บที่เหยื่อล็อกอินอยู่ โดยอาศัยว่าเบราว์เซอร์แนบ cookie ให้อัตโนมัติ บทนี้อธิบายเงื่อนไขที่เกิด, การสร้าง PoC (form/GET/XHR), บทบาทของ SameSite, และการ bypass การป้องกันที่ตั้งหลวม (token ไม่ผูก session, ลบ token, method/Referer หลวม, JSON content-type, CORS misconfig)
1. หลักการ & เงื่อนไข
เบราว์เซอร์แนบ cookie ของเว็บปลายทางให้ ทุก request โดยอัตโนมัติ แม้ request นั้นถูกสั่งจากเว็บอื่น ถ้าเว็บเป้าหมายตัดสิน 'ใคร' จาก cookie อย่างเดียวโดยไม่มี token ยืนยันว่า request มาจากหน้าเว็บของตัวเอง ผู้โจมตีสร้างหน้าเว็บที่ยิง request แทนเหยื่อได้ — เปลี่ยนอีเมล, โอนเงิน, เปลี่ยนรหัส, ผูกบัญชี
- เงื่อนไขที่ CSRF เกิด: (1) action มี side-effect, (2) พึ่ง cookie session อย่างเดียว, (3) ไม่มี token/parameter ที่คาดเดาไม่ได้
- ถ้าใช้ Authorization header / token ที่ JS ต้องแนบเอง → มักไม่โดน CSRF (เว็บอื่นแนบ header นั้นข้าม origin ไม่ได้)
- SameSite=Lax (ค่า default ของเบราว์เซอร์สมัยใหม่) ลด CSRF ลงมาก แต่ยังมีช่องผ่าน top-level GET และเทคนิคอื่น
2. สร้าง PoC
<form action="https://target/change-email" method="POST" id="f">
<input type="hidden" name="email" value="[email protected]">
</form>
<script>document.getElementById('f').submit()</script><img src="https://target/[email protected]">
<!-- หรือ top-level navigation ผ่าน SameSite=Lax -->
<script>location = 'https://target/action?x=1'</script><script>
fetch('https://target/api/change-email', {
method: 'POST',
credentials: 'include', // แนบ cookie ข้าม origin
headers: {'Content-Type':'text/plain'}, // เลี่ยง preflight
body: JSON.stringify({email:'[email protected]'})
})
</script>3. SameSite — ด่านแรกและช่องที่เหลือ
| SameSite | พฤติกรรม | ช่องที่เหลือ |
|---|---|---|
| Strict | ไม่แนบ cookie ทุก cross-site | ต้องหา on-site gadget (XSS/open redirect) |
| Lax (default) | แนบเฉพาะ top-level GET navigation | CSRF ผ่าน GET, หรือ method override |
| None | แนบทุก cross-site (ต้องมี Secure) | CSRF ได้เต็ม → ต้องพึ่ง token |
- Lax bypass ผ่าน GET: ถ้า state-changing action รับ GET หรือ method override → top-level navigation แนบ cookie ได้
- Lax 2-minute window: บางเบราว์เซอร์เคยแนบ cookie แบบ Lax กับ POST ในช่วงสั้นหลังตั้ง cookie (พฤติกรรมเปลี่ยนตามเวอร์ชัน)
- Sibling subdomain: SameSite มองที่ registrable domain — subdomain ที่ถูก compromise/มี XSS นับเป็น same-site
- Strict bypass: ต้องมี gadget บนโดเมนเดียวกัน (client-side redirect, XSS) ให้ยิงแบบ same-site
4. Bypass การป้องกันที่หลวม
แม้มี CSRF token ก็ยัง bypass ได้ถ้า implementation หลวม — ไล่ทดสอบทีละสมมติฐาน
| จุดอ่อน | ทดสอบ | ผล |
|---|---|---|
| token ไม่ผูก session | ใช้ token ของ attacker เองกับ session เหยื่อ | ผ่าน = bypass |
| token เช็คเฉพาะเมื่อมี | ลบ parameter token ออกทั้งหมด | ผ่าน = bypass |
| token เช็คต่อ method | เปลี่ยน POST→GET (token อาจไม่ถูกตรวจใน GET) | ผ่าน = bypass |
| token อยู่ใน cookie ด้วย (double-submit หลวม) | ตั้ง cookie+param เท่ากันจาก subdomain | ผ่าน = bypass |
| Referer เช็คหลวม | วาง target ใน path/param ของ evil (evil.com/target.com) | ผ่าน = bypass |
| Referer เช็คเฉพาะเมื่อมี | ตัด Referer ด้วย meta referrer / rel=noreferrer | ผ่าน = bypass |
<meta name="referrer" content="no-referrer">
<form action="https://target/action" method="POST">...</form>
<!-- request ออกไปโดยไม่มี Referer → ถ้า server เช็คเฉพาะเมื่อ Referer มี → ผ่าน -->JSON endpoint: API ที่รับเฉพาะ Content-Type: application/json ดูปลอดภัยขึ้นเพราะ form ส่ง json ไม่ได้ และ fetch ที่ตั้ง json จะ trigger CORS preflight แต่ถ้า backend ยอมรับ text/plain หรือไม่ตรวจ content-type จริง → ส่ง JSON ผ่าน text/plain (simple request ไม่ preflight) ได้
5. Decision flow
6. ตัวอย่าง CTF & Real-world
CTF (PortSwigger): เปลี่ยนอีเมลมี CSRF token แต่ 'token ไม่ผูก session' — เอา token จาก session ของเราเองใส่ใน PoC ที่ยิงด้วย cookie เหยื่อ → ผ่าน อีกโจทย์ token ถูกตรวจเฉพาะใน POST พอเปลี่ยนเป็น GET (method override) ระบบข้ามการเช็ค token ทั้งหมด
Real-world: API รับ application/json แต่ backend parse body โดยไม่บังคับ content-type — ส่งด้วย <form> ที่ตั้ง enctype=text/plain และประกอบ body ให้เป็น JSON ที่ valid ({"email":"[email protected]","ignore":"=1"}) → CSRF สำเร็จโดยไม่ต้องพึ่ง CORS
7. ข้อผิดพลาด & การป้องกัน
- สับสน CSRF กับ CORS: CORS ไม่ได้กัน CSRF — คนละเรื่อง
- สรุปว่า json = ปลอดภัย: ต้องตรวจว่า backend บังคับ content-type จริงไหม
- ลืมทดสอบ token binding: token มีอยู่ ≠ ผูก session/ถูกตรวจจริง
- มองข้าม SameSite bypass: Lax ยังโดน GET/method override
SameSite=Lax/Strict, ตรวจ Origin header (แม่นกว่า Referer และตัดยาก), บังคับ content-type สำหรับ JSON API, ใช้ custom header ที่ต้องตั้งด้วย JS same-origin, และไม่ยอมรับ state-changing action ผ่าน GET8. Quick Reference
- เกิดเมื่อ: side-effect + พึ่ง cookie อย่างเดียว + ไม่มี token
- PoC: auto-submit form,
(GET), fetch credentials:include
- SameSite: Strict→หา gadget, Lax→GET/method override, None→ต้องมี token
- bypass token: ลบ token, ใช้ token attacker, สลับ method, double-submit หลวม
- Referer หลวม: ตัด Referer / วาง target ใน path ของ evil
- JSON: ลอง text/plain (simple request เลี่ยง preflight)
- CORS ≠ ป้องกัน CSRF; Authorization header = มักกันได้
- ป้องกัน: token ผูก session + SameSite + ตรวจ Origin + บังคับ content-type
🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ
สมมติเจอ action ที่มี side-effect (เปลี่ยนอีเมล/โอนเงิน) มีแค่ Kali เปล่าๆ ทำตามนี้ทีละขั้นเพื่อเช็คว่ามี CSRF ไหม
- 1เปิด Burp Suite ตั้ง proxy แล้วทำ action ที่มี side-effect ผ่านหน้าเว็บ ให้ Burp ดักไว้ 1 request
- 2เช็ค Set-Cookie ของ session ด้วย curl -sI ดูว่า SameSite เป็น Strict/Lax/None
- 3ดูว่า request พึ่ง cookie อย่างเดียวไหม (ไม่มี Authorization header หรือ custom token ที่ JS ต้องแนบเอง)
- 4ใน Burp คลิกขวา request → Engagement tools → Generate CSRF PoC เพื่อสร้าง HTML form อัตโนมัติ
- 5เซฟไฟล์ PoC แล้วเปิดด้วย python3 -m http.server ในเบราว์เซอร์ที่ยัง login เว็บเป้าหมายอยู่ ดูว่า action ทำงานไหม
- 6ถ้ามี CSRF token ลองลบ parameter token ออกทั้งหมดแล้วส่งซ้ำผ่าน Repeater
- 7ลองใช้ token จาก session ของตัวเองแทนของเหยื่อ (เช็คว่าผูก session จริงไหม)
- 8ลองเปลี่ยน POST→GET ดูว่า token ยังถูกตรวจไหม
- 9ถ้า endpoint เป็น JSON ลองเปลี่ยน Content-Type เป็น text/plain แล้วประกอบ body ให้ยังเป็น JSON ที่ valid
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| ดักจับ request + สร้าง PoC | Burp Suite Community (Generate CSRF PoC) | - | - |
| ตรวจ cookie SameSite | curl | - | - |
| โฮสต์ PoC HTML ทดสอบ | python3 -m http.server | - | - |
| ทดสอบ bypass content-type/method | Burp Repeater | - | - |
| หา gadget เสริม (XSS) เมื่อ Strict | - | - | - |
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้