ป้องกันข้อมูลรั่วไหล CSRF, XSSI และข้ามต้นทาง
เหตุใดคุณจึงควรสนใจการแยกทรัพยากรบนเว็บ
เว็บแอปพลิเคชันจำนวนมากมีความเสี่ยงต่อการโจมตีข้ามแหล่งที่มา เช่น การปลอมแปลงคำขอข้ามเว็บไซต์ (CSRF), การรวมสคริปต์ข้ามเว็บไซต์ (XSSI), การโจมตีตามช่วงเวลา, การโจมตีช่องทางด้านข้างของการดำเนินการแบบคาดการณ์ (Spectre) หรือข้อมูลรั่วไหลข้ามแหล่งที่มา
ส่วนหัวคำขอดึงข้อมูลเมตาช่วยให้คุณใช้กลไกการป้องกันแบบหลายชั้นที่มีประสิทธิภาพ ซึ่งก็คือนโยบายการแยกทรัพยากร เพื่อปกป้องแอปพลิเคชันจากการโจมตีข้ามโดเมนที่พบได้ทั่วไปเหล่านี้
เป็นเรื่องปกติที่ทรัพยากรที่มาจากเว็บแอปพลิเคชันหนึ่งๆ จะโหลดโดยตัวแอปพลิเคชันเท่านั้น ไม่ใช่โดยเว็บไซต์อื่น ในกรณีเช่นนี้ การทำให้นโยบายการแยกทรัพยากรใช้งานได้โดยอิงตามส่วนหัวของคำขอดึงข้อมูลข้อมูลเมตานั้นใช้ความพยายามเพียงเล็กน้อย และในขณะเดียวกันก็ปกป้องแอปพลิเคชันจากการโจมตีแบบข้ามเว็บไซต์ด้วย
ความเข้ากันได้กับเบราว์เซอร์
เบราว์เซอร์สมัยใหม่ทั้งหมดรองรับส่วนหัวของคำขอข้อมูลเมตาที่ดึงข้อมูล
ข้อมูลเบื้องต้น
การโจมตีข้ามเว็บไซต์อาจเกิดขึ้นได้ เนื่องจากเว็บเปิดอยู่โดยค่าเริ่มต้น และเซิร์ฟเวอร์แอปพลิเคชันของคุณไม่สามารถป้องกันตัวเองจากการสื่อสารที่มาจากแอปพลิเคชันภายนอกได้โดยง่าย การโจมตีแบบข้ามต้นทางโดยทั่วไปคือการปลอมแปลงคำขอแบบข้ามเว็บไซต์ (CSRF) ซึ่งผู้โจมตีจะล่อลวงผู้ใช้ลงบนเว็บไซต์ที่ตนควบคุมอยู่ แล้วส่งแบบฟอร์มไปยังเซิร์ฟเวอร์ที่ผู้ใช้เข้าสู่ระบบอยู่ เนื่องจากเซิร์ฟเวอร์ไม่สามารถบอกได้ว่าคำขอมาจากโดเมนอื่น (ข้ามเว็บไซต์) หรือไม่ และเบราว์เซอร์จะแนบคุกกี้ไปกับคำขอแบบข้ามเว็บไซต์โดยอัตโนมัติ เซิร์ฟเวอร์จะดำเนินการตามที่ผู้โจมตีร้องขอในนามของผู้ใช้
การโจมตีข้ามเว็บไซต์อื่นๆ เช่น การรวมสคริปต์ข้ามเว็บไซต์ (XSSI) หรือการรั่วไหลของข้อมูลข้ามต้นทางนั้นมีลักษณะคล้ายกับ CSRF และอาศัยทรัพยากรการโหลดจากแอปพลิเคชันของเหยื่อในเอกสารที่ผู้โจมตีควบคุมและข้อมูลรั่วไหลเกี่ยวกับแอปพลิเคชันของเหยื่อ เนื่องจากแอปพลิเคชันไม่สามารถแยกแยะคำขอที่เชื่อถือได้กับคำขอที่ไม่น่าเชื่อถือได้โดยง่าย จึงไม่สามารถทิ้งการเข้าชมแบบข้ามเว็บไซต์ที่เป็นอันตรายได้
ขอแนะนำการดึงข้อมูลข้อมูลเมตา
ส่วนหัวคำขอข้อมูลเมตาแบบดึงข้อมูลเป็นฟีเจอร์ความปลอดภัยใหม่ของแพลตฟอร์มเว็บที่ออกแบบมาเพื่อช่วยเซิร์ฟเวอร์ป้องกันตัวเองจากการโจมตีข้ามแหล่งที่มา การให้ข้อมูลเกี่ยวกับบริบทของคำขอ HTTP ในชุดส่วนหัว Sec-Fetch-*
ช่วยให้เซิร์ฟเวอร์ที่ตอบสนองใช้นโยบายความปลอดภัยได้ก่อนประมวลผลคำขอ สิทธิ์นี้เปิดโอกาสให้นักพัฒนาแอปตัดสินใจว่าจะยอมรับหรือปฏิเสธคําขอโดยอิงตามรูปแบบที่ส่งคำขอและบริบทในการใช้งานคำขอ ทำให้ตอบกลับได้เฉพาะคำขอที่ถูกต้องจากแอปพลิเคชันของตนเองเท่านั้น
Sec-Fetch-Site
Sec-Fetch-Site
บอกเซิร์ฟเวอร์ว่าเว็บไซต์ใดส่งคำขอ โดยเบราว์เซอร์จะตั้งค่านี้เป็นค่าใดค่าหนึ่งต่อไปนี้
same-origin
หากเป็นคำขอจากแอปพลิเคชันของคุณเอง (เช่นsite.example
)same-site
หากคำขอมาจากโดเมนย่อยของเว็บไซต์ (เช่นbar.site.example
)none
หากคำขอนั้นเกิดจากการโต้ตอบของผู้ใช้กับ User Agent อย่างชัดเจน (เช่น การคลิกบุ๊กมาร์ก)cross-site
หากคำขอส่งมาจากเว็บไซต์อื่น (เช่นevil.example
)
Sec-Fetch-Mode
Sec-Fetch-Mode
ระบุโหมดของคําขอ ซึ่งจะสอดคล้องกับประเภทของคำขออย่างคร่าวๆ และช่วยให้คุณแยกความแตกต่างของการโหลดทรัพยากรกับคำขอการนำทางได้ เช่น ปลายทาง navigate
หมายถึงคําขอการนําทางระดับบนสุด ส่วน no-cors
หมายถึงคําขอทรัพยากร เช่น การโหลดรูปภาพ
Sec-Fetch-Dest
Sec-Fetch-Dest
จะแสดงปลายทางของคำขอ (เช่น หากแท็ก script
หรือ img
ทำให้เบราว์เซอร์ขอทรัพยากร)
วิธีใช้การดึงข้อมูลเมตาเพื่อปกป้องจากการโจมตีข้ามแหล่งที่มา
ข้อมูลเพิ่มเติมที่ส่วนหัวของคำขอเหล่านี้ให้ไว้นั้นค่อนข้างเรียบง่าย แต่บริบทเพิ่มเติมจะช่วยให้คุณสร้างตรรกะการรักษาความปลอดภัยที่มีประสิทธิภาพในฝั่งเซิร์ฟเวอร์ได้ หรือที่เรียกว่านโยบายการแยกทรัพยากร โดยใช้โค้ดเพียงไม่กี่บรรทัด
การใช้นโยบายการแยกทรัพยากร
นโยบายการแยกทรัพยากรจะป้องกันไม่ให้เว็บไซต์ภายนอกขอทรัพยากรของคุณ การบล็อกการรับส่งข้อมูลดังกล่าวจะช่วยลดช่องโหว่ทั่วไปบนเว็บแบบข้ามเว็บไซต์ เช่น CSRF, XSSI, การโจมตีแบบจับเวลา และข้อมูลรั่วไหลแบบข้ามต้นทาง คุณเปิดใช้นโยบายนี้สำหรับปลายทางทั้งหมดของแอปพลิเคชันได้และจะอนุญาตคำขอทรัพยากรทั้งหมดที่มาจากแอปพลิเคชันของคุณเอง รวมถึงการนำทางโดยตรง (ผ่านคำขอ HTTP GET
) ปลายทางที่ควรจะโหลดในบริบทข้ามเว็บไซต์ (เช่น ปลายทางที่โหลดโดยใช้ CORS) สามารถเลือกไม่ใช้ตรรกะนี้ได้
ขั้นตอนที่ 1: อนุญาตคําขอจากเบราว์เซอร์ที่ไม่ได้ส่งข้อมูลเมตาการดึงข้อมูล
เนื่องจากบางเบราว์เซอร์ไม่รองรับการดึงข้อมูลข้อมูลเมตา คุณจึงต้องอนุญาตคำขอที่ไม่ได้ตั้งค่าส่วนหัว Sec-Fetch-*
โดยตรวจหาการมีอยู่ของ sec-fetch-site
if not req['sec-fetch-site']:
return True # Allow this request
ขั้นตอนที่ 2: อนุญาตคำขอที่มาจากเว็บไซต์เดียวกันและเบราว์เซอร์ที่เริ่มต้น
คำขอที่ไม่ได้มาจากบริบทแบบข้ามต้นทาง (เช่น evil.example
) จะได้รับอนุญาต โดยเฉพาะอย่างยิ่ง คำขอเหล่านี้ต้องมีลักษณะดังนี้
- มาจากแอปพลิเคชันของคุณเอง (เช่น คำขอต้นทางเดียวกันโดยที่
site.example
จะอนุญาตคำขอsite.example/foo.json
เสมอ) - มาจากโดเมนย่อย
- เกิดจากการโต้ตอบของผู้ใช้กับ User Agent อย่างชัดเจน (เช่น การไปยังส่วนต่างๆ โดยตรงหรือการคลิกบุ๊กมาร์ก เป็นต้น)
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True # Allow this request
ขั้นตอนที่ 3: อนุญาตการนําทางระดับบนสุดแบบง่ายและการฝังเฟรม
เพื่อให้มั่นใจว่าเว็บไซต์ของคุณจะยังลิงก์จากเว็บไซต์อื่นๆ ได้ คุณต้องอนุญาตการนำทางระดับบนสุดแบบง่าย (HTTP GET
)
if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
# <object> and <embed> send navigation requests, which we disallow.
and req['sec-fetch-dest'] not in ('object', 'embed'):
return True # Allow this request
ขั้นตอนที่ 4: เลือกไม่ใช้ปลายทางที่มีไว้เพื่อแสดงการเข้าชมข้ามเว็บไซต์ (ไม่บังคับ)
ในบางกรณี แอปพลิเคชันอาจให้ทรัพยากรที่มีไว้เพื่อโหลดข้ามเว็บไซต์ ทรัพยากรเหล่านี้ต้องได้รับการยกเว้นตามเส้นทางหรือปลายทาง ตัวอย่างของปลายทางดังกล่าว ได้แก่
- ปลายทางที่มีไว้สำหรับการเข้าถึงข้ามแหล่งที่มา: หากแอปพลิเคชันแสดงปลายทางที่เปิดใช้
CORS
คุณต้องเลือกไม่ใช้ปลายทางเหล่านี้จากการแยกทรัพยากรอย่างชัดเจนเพื่อให้คำขอข้ามเว็บไซต์ไปยังปลายทางเหล่านี้ยังคงเป็นไปได้ - แหล่งข้อมูลสาธารณะ (เช่น รูปภาพ สไตล์ ฯลฯ) ทรัพยากรสาธารณะและที่ไม่ได้ตรวจสอบสิทธิ์ใดๆ ที่ควรโหลดแบบข้ามต้นทางจากเว็บไซต์อื่นๆ ได้สามารถยกเว้นได้เช่นกัน
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
return True
ขั้นตอนที่ 5: ปฏิเสธคำขออื่นๆ ทั้งหมดที่เป็นข้ามเว็บไซต์และไม่ใช้การนำทาง
นโยบายการแยกทรัพยากรนี้จะปฏิเสธคำขอข้ามเว็บไซต์อื่นๆ ทั้งหมด และปกป้องแอปพลิเคชันจากการโจมตีข้ามเว็บไซต์ที่พบได้ทั่วไป
ตัวอย่าง: โค้ดต่อไปนี้แสดงการใช้งานนโยบายการแยกทรัพยากรที่มีประสิทธิภาพบนเซิร์ฟเวอร์หรือเป็นมิดเดิลแวร์เพื่อปฏิเสธคำขอทรัพยากรแบบข้ามเว็บไซต์ที่อาจเป็นอันตราย ในขณะเดียวกันก็อนุญาตคำขอการนำทางแบบง่ายๆ
# Reject cross-origin requests to protect from CSRF, XSSI, and other bugs
def allow_request(req):
# Allow requests from browsers which don't send Fetch Metadata
if not req['sec-fetch-site']:
return True
# Allow same-site and browser-initiated requests
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True
# Allow simple top-level navigations except <object> and <embed>
if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
and req['sec-fetch-dest'] not in ('object', 'embed'):
return True
# [OPTIONAL] Exempt paths/endpoints meant to be served cross-origin.
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
return True
# Reject all other requests that are cross-site and not navigational
return False
การใช้นโยบายการแยกทรัพยากร
- ติดตั้งโมดูล เช่น ข้อมูลโค้ดจากด้านบน เพื่อบันทึกและตรวจสอบลักษณะการทำงานของเว็บไซต์ และตรวจสอบว่าข้อจํากัดไม่ส่งผลต่อการเข้าชมที่ถูกต้อง
- แก้ไขการละเมิดที่อาจเกิดขึ้นโดยการยกเว้นปลายทางข้ามแหล่งที่มาที่ถูกต้อง
- บังคับใช้นโยบายโดยส่งคำขอที่ไม่เป็นไปตามข้อกำหนด
การระบุและการแก้ไขการละเมิดนโยบาย
เราขอแนะนำให้คุณทดสอบนโยบายในลักษณะที่ไม่มีผลกระทบ โดยการเปิดใช้นโยบายในโหมดการรายงานในโค้ดฝั่งเซิร์ฟเวอร์ก่อน อีกทางเลือกหนึ่งคือ คุณสามารถใช้ตรรกะนี้ในมิดเดิลแวร์ หรือในพร็อกซีย้อนกลับซึ่งบันทึกการละเมิดที่นโยบายอาจเกิดขึ้นเมื่อใช้กับการรับส่งข้อมูลที่ใช้งานจริง
จากประสบการณ์ของเราในการเปิดตัวนโยบายการแยกทรัพยากรข้อมูลเมตาที่ Google โดยค่าเริ่มต้น แอปพลิเคชันส่วนใหญ่สามารถเข้ากันได้กับนโยบายดังกล่าว และแทบไม่จำเป็นต้องมีปลายทางที่ยกเว้นเพื่ออนุญาตการรับส่งข้อมูลข้ามเว็บไซต์
การบังคับใช้นโยบายการแยกทรัพยากร
หลังจากตรวจสอบแล้วว่านโยบายไม่ส่งผลกระทบต่อปริมาณการใช้งานจริงที่ถูกต้องตามกฎหมาย คุณก็พร้อมที่จะบังคับใช้ข้อจำกัดต่างๆ และรับประกันได้ว่าเว็บไซต์อื่นๆ จะไม่สามารถขอทรัพยากรของคุณและปกป้องผู้ใช้จากการโจมตีแบบข้ามเว็บไซต์ได้
อ่านเพิ่มเติม
- ข้อกำหนดส่วนหัวของคำขอข้อมูลเมตาการดึงข้อมูลของ W3C
- ดึงข้อมูลข้อมูลเมตา Playground
- พูดคุยเรื่อง Google I/O: การรักษาความปลอดภัยให้กับเว็บแอปด้วยฟีเจอร์แพลตฟอร์มสมัยใหม่ (สไลด์)