ปกป้องทรัพยากรจากการโจมตีเว็บด้วยการดึงข้อมูลข้อมูลเมตา

ป้องกัน CSRF, XSSI และข้อมูลรั่วไหลข้ามแหล่งที่มา

เหตุใดคุณจึงควรแยกทรัพยากรเว็บ

เว็บแอปพลิเคชันจำนวนมากมีความเสี่ยงต่อการโจมตีข้ามแหล่งที่มา เช่น การปลอมแปลงคำขอข้ามเว็บไซต์ (CSRF), การรวมสคริปต์ข้ามเว็บไซต์ (XSSI), การโจมตีตามช่วงเวลา, การโจมตีช่องทางด้านข้างของการดำเนินการแบบคาดการณ์ (Spectre) หรือข้อมูลรั่วไหลข้ามแหล่งที่มา

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

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

ความเข้ากันได้กับเบราว์เซอร์

เบราว์เซอร์สมัยใหม่ทั้งหมดรองรับส่วนหัวของคำขอข้อมูลเมตาที่ดึงข้อมูล

การรองรับเบราว์เซอร์

  • Chrome: 76
  • Edge: 79
  • Firefox: 90
  • Safari: 16.4

แหล่งที่มา

ข้อมูลเบื้องต้น

การโจมตีข้ามเว็บไซต์จำนวนมากเกิดขึ้นได้เนื่องจากเว็บเปิดอยู่โดยค่าเริ่มต้นและเซิร์ฟเวอร์แอปพลิเคชันของคุณไม่สามารถปกป้องตนเองจากการติดต่อสื่อสารที่มาจากแอปพลิเคชันภายนอกได้ง่ายๆ การโจมตีข้ามแหล่งที่มาที่พบได้ทั่วไปคือการปลอมแปลงคำขอข้ามเว็บไซต์ (CSRF) ซึ่งผู้โจมตีจะหลอกล่อให้ผู้ใช้ไปยังเว็บไซต์ที่ตนควบคุม จากนั้นส่งแบบฟอร์มไปยังเซิร์ฟเวอร์ที่ผู้ใช้เข้าสู่ระบบอยู่ เนื่องจากเซิร์ฟเวอร์ไม่สามารถบอกได้ว่าคำขอนั้นมาจากโดเมนอื่น (ข้ามเว็บไซต์) หรือไม่ และเบราว์เซอร์จะแนบคุกกี้ไปกับคำขอข้ามเว็บไซต์โดยอัตโนมัติ เซิร์ฟเวอร์จึงจะดำเนินการตามที่ผู้โจมตีร้องขอในนามของผู้ใช้

การโจมตีข้ามเว็บไซต์อื่นๆ เช่น การรวมสคริปต์ข้ามเว็บไซต์ (XSSI) หรือการเปิดเผยข้อมูลข้ามแหล่งที่มามีลักษณะคล้ายกับ CSRF และอาศัยการโหลดทรัพยากรจากแอปพลิเคชันเหยื่อในเอกสารที่ผู้โจมตีควบคุม และมีการเปิดเผยข้อมูลเกี่ยวกับแอปพลิเคชันเหยื่อ เนื่องจากแอปพลิเคชันไม่สามารถแยกแยะคำขอที่เชื่อถือได้ออกจากคำขอที่ไม่น่าเชื่อถือได้ง่ายๆ แอปพลิเคชันจึงไม่สามารถทิ้งการเข้าชมข้ามเว็บไซต์ที่เป็นอันตราย

ขอแนะนําฟีเจอร์ดึงข้อมูลเมตา

ส่วนหัวคำขอข้อมูลเมตาแบบดึงข้อมูลเป็นฟีเจอร์ความปลอดภัยใหม่ของแพลตฟอร์มเว็บที่ออกแบบมาเพื่อช่วยเซิร์ฟเวอร์ป้องกันตัวเองจากการโจมตีข้ามแหล่งที่มา การให้ข้อมูลเกี่ยวกับบริบทของคำขอ HTTP ในชุดส่วนหัว Sec-Fetch-* จะช่วยให้เซิร์ฟเวอร์ที่ตอบกลับใช้นโยบายความปลอดภัยก่อนที่จะประมวลผลคำขอได้ ซึ่งจะช่วยให้นักพัฒนาแอปตัดสินใจว่าจะยอมรับหรือปฏิเสธคำขอโดยพิจารณาจากวิธีสร้างคำขอและบริบทที่จะใช้คำขอ ซึ่งทำให้สามารถตอบกลับเฉพาะคำขอที่ถูกต้องซึ่งสร้างโดยแอปพลิเคชันของตนเองได้

มาจากต้นทางเดียวกัน
คําขอที่มาจากเว็บไซต์ที่แสดงโดยเซิร์ฟเวอร์ของคุณเอง (ต้นทางเดียวกัน) จะยังคงใช้งานได้ต่อไป คำขอดึงข้อมูลจาก https://site.example สำหรับทรัพยากร https://site.example/foo.json ใน JavaScript ทําให้เบราว์เซอร์ส่งส่วนหัวคําขอ HTTP "Sec Fetch-Site: same-origin"
ข้ามเว็บไซต์
เซิร์ฟเวอร์สามารถปฏิเสธคำขอข้ามเว็บไซต์ที่เป็นอันตรายได้เนื่องจากบริบทเพิ่มเติมในคำขอ HTTP ที่ได้จากส่วนหัว Sec-Fetch-* รูปภาพใน https://evil.example ที่ตั้งค่าแอตทริบิวต์ src ขององค์ประกอบ img เป็น "https://site.example/foo.json" ทําให้เบราว์เซอร์ส่งส่วนหัวคําขอ HTTP "Sec-Fetch-Site: cross-site"

Sec-Fetch-Site

การรองรับเบราว์เซอร์

  • Chrome: 76
  • Edge: 79
  • Firefox: 90
  • Safari: 16.4

แหล่งที่มา

Sec-Fetch-Site บอกเซิร์ฟเวอร์ว่าเว็บไซต์ใดส่งคำขอ เบราว์เซอร์จะตั้งค่านี้เป็นค่าใดค่าหนึ่งต่อไปนี้

  • same-origin หากคำขอมาจากแอปพลิเคชันของคุณเอง (เช่น site.example)
  • same-site หากคำขอมาจากโดเมนย่อยของเว็บไซต์ (เช่น bar.site.example)
  • none หากคำขอเกิดจากการกระทำของผู้ใช้กับ User Agent อย่างชัดเจน (เช่น การคลิกบุ๊กมาร์ก)
  • cross-site หากคำขอส่งมาจากเว็บไซต์อื่น (เช่น evil.example)

Sec-Fetch-Mode

การรองรับเบราว์เซอร์

  • Chrome: 76
  • Edge: 79
  • Firefox: 90
  • Safari: 16.4

แหล่งที่มา

Sec-Fetch-Mode ระบุโหมดของคําขอ ซึ่งจะสอดคล้องกับประเภทของคำขอโดยคร่าวๆ และช่วยให้คุณแยกการโหลดทรัพยากรออกจากคำขอการนําทางได้ เช่น ปลายทาง navigate หมายถึงคําขอการนําทางระดับบนสุด ส่วน no-cors หมายถึงคําขอทรัพยากร เช่น การโหลดรูปภาพ

Sec-Fetch-Dest

การรองรับเบราว์เซอร์

  • Chrome: 80
  • Edge: 80
  • Firefox: 90
  • Safari: 16.4

แหล่งที่มา

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

การใช้นโยบายการแยกทรัพยากร

  1. ติดตั้งโมดูล เช่น ข้อมูลโค้ดจากด้านบน เพื่อบันทึกและตรวจสอบลักษณะการทำงานของเว็บไซต์ และตรวจสอบว่าข้อจํากัดไม่ส่งผลต่อการเข้าชมที่ถูกต้อง
  2. แก้ไขการละเมิดที่อาจเกิดขึ้นโดยการยกเว้นปลายทางข้ามแหล่งที่มาที่ถูกต้อง
  3. บังคับใช้นโยบายโดยทิ้งคำขอที่ไม่เป็นไปตามข้อกำหนด

การระบุและการแก้ไขการละเมิดนโยบาย

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

จากประสบการณ์ในการเปิดตัวนโยบายการแยกทรัพยากรข้อมูลเมตาที่ Google แอปพลิเคชันส่วนใหญ่เข้ากันได้กับนโยบายดังกล่าวโดยค่าเริ่มต้น และแทบไม่จําเป็นต้องยกเว้นปลายทางเพื่ออนุญาตการเข้าชมข้ามเว็บไซต์

การบังคับใช้นโยบายการแยกทรัพยากร

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

อ่านเพิ่มเติม