ทำไมจึงต้องใช้ทั้งการป้องกัน CSRF และ CORS?
(smagin.fyi)- ตอนที่เริ่มพิจารณาเรื่อง Cross-Site Request ใหม่ ๆ ก็ยังไม่เข้าใจในตอนแรกว่าทำไมถึงต้องมีทั้งการป้องกัน CSRF และ CORS พร้อมกัน แต่ถ้าจะอธิบายเรื่องนี้ต้องใช้คำค่อนข้างมาก
CSRF และ CORS
- CSRF (Cross-Site Request Forgery)
- ในอดีตเคยพบได้บ่อย แต่ปัจจุบันเว็บเฟรมเวิร์กส่วนใหญ่มีการป้องกันมาให้เป็นค่าเริ่มต้น จึงแทบไม่ค่อยเป็นปัญหาแล้ว
- วิธีโจมตี: หลอกให้ผู้ใช้คลิกฟอร์มบางอย่างบนเว็บไซต์อันตราย เพื่อส่งคำขอแบบข้ามไซต์
- วิธีป้องกัน: ตรวจสอบว่าคำขอนั้นไม่ได้ไหลเข้ามาจากเว็บไซต์อื่น
- CORS (Cross-Origin Resource Sharing)
- เป็นส่วนหนึ่งของสเปก HTTP ที่กำหนดวิธีอนุญาตคำขอข้ามไซต์บางประเภท
- ใช้ preflight request และ response header เพื่อระบุว่าอนุญาตให้ส่งคำขอจาก origin ใดได้บ้าง
ถ้าอย่างนั้น คำขอข้ามไซต์ถูกอนุญาตเป็นค่าเริ่มต้นจนต้องมี CSRF protection หรือจริง ๆ แล้วถูกบล็อกเป็นค่าเริ่มต้นและต้องใช้ CORS เพื่อเปิดให้ใช้งาน? คำตอบคือ ทั้งสองอย่าง
พฤติกรรมพื้นฐาน
- นโยบาย same-origin (Same-origin policy)
- เป็นนโยบายความปลอดภัยที่เบราว์เซอร์บังคับใช้
- โดยทั่วไป การเขียน (Write) แบบข้ามไซต์ทำได้, แต่ การอ่าน (Read) ถูกห้าม
- ตัวอย่างเช่น เบราว์เซอร์อนุญาตคำขอ POST ผ่านฟอร์มได้ แต่ไม่อนุญาตให้อ่าน response
- นโยบายคุกกี้ SameSite
- ในปี 2019 พฤติกรรมเริ่มต้นของคุกกี้ได้เปลี่ยนไป
- เดิมทีคุกกี้จะถูกส่งไปพร้อมคำขอข้ามไซต์เสมอ
- มีการเพิ่มแอตทริบิวต์
SameSiteใหม่ และเปลี่ยนค่าเริ่มต้นเป็นLax - ณ ปี 2025 96% ของเบราว์เซอร์รองรับแอตทริบิวต์
SameSiteและ 75% รองรับค่าเริ่มต้นใหม่ (Lax) - อย่างไรก็ตาม Safari ไม่ได้นำค่าเริ่มต้นนี้มาใช้ และ UCBrowser ก็ยังไม่รองรับ
- ความแตกต่างระหว่าง Site กับ Origin
- Origin: การรวมกันของ
โปรโตคอล + ชื่อโฮสต์ + พอร์ต - Site: การรวมกันของ
โปรโตคอล + top-level domain + 1(ไม่สนใจซับโดเมนและพอร์ต)
- Origin: การรวมกันของ
CORS
- CORS คือวิธีเปิดข้อยกเว้นของนโยบาย same-origin ให้กับ origin บางแห่ง
- ก่อนส่งคำขอ เบราว์เซอร์จะส่ง preflight request แบบ
OPTIONSก่อน - เซิร์ฟเวอร์จะกำหนดกฎการอนุญาตผ่าน response header (ใช้ header กลุ่ม
Access-Control-*) - ประเภทคำขอที่ CORS มีผลบังคับใช้:
fetchและXMLHttpRequest- เว็บฟอนต์
- WebGL texture
- รูปภาพ/เฟรมวิดีโอที่วาดด้วย
drawImageในcanvas - รูปภาพที่ใช้ในพร็อพเพอร์ตี CSS
shape-outside
- แต่การส่งฟอร์มเป็นข้อยกเว้นที่ CORS ไม่ครอบคลุม
- แท็ก
<form>ของ HTML 4.0 อนุญาตคำขอข้ามไซต์มานานมากแล้ว - ดังนั้นเซิร์ฟเวอร์รุ่นเก่าจึงควรถูกออกแบบมาให้ป้องกันการโจมตีแบบ CSRF อยู่แล้ว
- หากต้องการแชร์ response เซิร์ฟเวอร์ต้องตั้งค่า
Access-Control-Allow-Originแต่ตัวคำขอเองยังคงถูกรับได้แม้ไม่มี preflight request
- แท็ก
คำถาม: นโยบาย
SameSiteกับวิธีนี้รักษาความสอดคล้องกันอย่างไร?
วิธีป้องกัน CSRF
- คำขอแบบเขียนข้ามไซต์ได้รับอนุญาต แต่ response จะไม่ถูกแชร์
- เว็บไซต์ส่วนใหญ่ไม่ได้ต้องการอนุญาตการเขียนแบบข้ามไซต์
- แนวทางป้องกัน CSRF มาตรฐาน
- ใส่ CSRF token รายผู้ใช้ไว้ในคำขอเพื่อนำไปตรวจสอบ
- วิธีการ:
- การส่งฟอร์ม: เพิ่มโทเคนด้วย hidden input
- คำขอจาก JS: เก็บไว้ในคุกกี้หรือแท็ก
metaแล้วส่งไปใน request header หรือพารามิเตอร์
- คำขอจาก JS โดยปกติถูกบล็อกไม่ให้ข้ามไซต์อยู่แล้ว
- แต่ยังอนุญาตสำหรับคำขอแบบ same-site
- หากใส่ CSRF token ก็สามารถตรวจสอบคำขอทั้งหมดด้วยวิธีเดียวกันได้
- ข้อดีด้านความปลอดภัยเพิ่มเติม
- ทำงานบนสมมติฐานว่าเบราว์เซอร์จะบล็อกการอ่าน response เป็นค่าเริ่มต้น
- ปลอดภัยกว่าการตรวจสอบ header
Originเพียงอย่างเดียว
คำถาม: ในบางเฟรมเวิร์กมีการเปลี่ยน CSRF token เป็นระยะ ๆ ทำไมจึงเป็นเช่นนั้น?
บทบาทของเบราว์เซอร์
- หัวใจสำคัญของความปลอดภัยบนเว็บขึ้นอยู่กับว่า เบราว์เซอร์เชื่อถือได้หรือไม่
- เบราว์เซอร์จะ:
- บังคับใช้นโยบาย same-origin
- บล็อกไม่ให้อ่าน response หากไม่ได้รับอนุญาต
- ตัดสินใจว่าจะใช้ค่าเริ่มต้น
SameSite=Laxหรือไม่ - ติดตั้งใช้งาน CORS และส่ง preflight request อย่างปลอดภัย
เราจำเป็นต้องเชื่อถือเบราว์เซอร์ที่เราใช้งานอยู่
สรุป
- หาก
SameSite=Laxได้รับการรองรับครบ 100% ในทุกเบราว์เซอร์ ความปลอดภัยก็จะสูงขึ้นอีก
แต่ในปัจจุบันก็ยังคงมีข้อยกเว้นที่อนุญาตเฉพาะ คำขอ POST แบบข้ามไซต์ อยู่ - ดังนั้นนักพัฒนาจึงยังต้องคำนึงถึงการป้องกัน CSRF อย่างต่อเนื่อง
"อินเทอร์เน็ตกำลังปลอดภัยขึ้นเรื่อย ๆ แต่ในขณะเดียวกัน ความเข้ากันได้กับอดีตก็กำลังลดลงเรื่อย ๆ เช่นกัน"
1 ความคิดเห็น
ความเห็นจาก Hacker News
CORS เป็นกลไกที่เซิร์ฟเวอร์ใช้บอกเบราว์เซอร์อย่างชัดเจนว่าคำขอข้ามต้นทางแบบใดบ้างที่สามารถอ่านการตอบกลับได้
การป้องกัน CSRF ช่วยป้องกันไม่ให้คำขอข้ามต้นทางที่เป็นอันตรายดำเนินการโดยไม่ได้รับอนุญาตในนามของผู้ใช้ที่ยืนยันตัวตนแล้ว
การป้องกัน CSRF คือเรื่องของการป้องกันการเขียน ส่วน CORS คือเรื่องของการป้องกันการอ่าน
คำขอที่เริ่มต้นจาก JS จะไม่อนุญาตให้ข้ามไซต์โดยค่าเริ่มต้น
fetch()หากใช้เฉพาะเฮดเดอร์ที่ได้รับอนุญาตคิดว่าน่าจะมีคำอธิบายที่ดีกว่านี้สำหรับหัวข้อนี้
คำตอบต่อคำถามในโพสต์บล็อก
<form>ของ HTML 4.0 สามารถส่ง simple request ไปยังต้นทางใดก็ได้ในปี 2022 มีการเพิ่มย่อหน้าในบทความ CORS ของ MDN เพื่อชี้แจงที่มาของคำว่า "simple request"
น่าสับสนที่ SameSite ถูกเพิ่มเข้ามาโดยเป็นอิสระจาก CORS preflight
อาจคิดว่าปลอดภัยแม้ไม่ใช้ csrf แต่บางไลบรารี (เช่น django rest framework) สามารถประมวลผลฟอร์ม HTML ได้หากมีการตั้งค่า content-type header
คำถามเกี่ยวกับเหตุผลที่ CSRF token มีการหมุนเปลี่ยน
ขอผังงานสำหรับหัวข้อที่ซับซ้อนนี้
สิ่งเหล่านี้ไม่รองรับการติดตามเพื่อวินิจฉัยที่ทำได้ง่าย
ไม่เข้าใจว่าก่อนที่ CORS จะมีขึ้นมา ทำไมจึงส่งคำขอไปยังเอนด์พอยต์ใดก็ได้ที่ไม่ใช่ต้นทางของหน้าได้ แต่กลับดูการตอบกลับไม่ได้
ความสับสนเกี่ยวกับการป้องกัน CSRF