กลับไปหน้าบทความ
#CORS#Web Security#API Security#Browser#Access Control

CORS ไม่ใช่กำแพงกันแฮกเกอร์: วิธีตั้งค่าให้ปลอดภัยและไม่เปิดอ่านข้อมูล

CORS เป็นกติกาที่กำหนดว่าเบราว์เซอร์จะยอมให้เว็บใดอ่าน response จาก API ได้ ไม่ใช่กลไกป้องกันการโจมตีโดยตรง ดังนั้นหากตั้งค่า Access-Control-Allow-Origin กว้างเกินไป โดยเฉพาะเมื่อใช้ร่วมกับ credentials ก็อาจเปิดช่องให้เว

28 กรกฎาคม 2569อ่านประมาณ 3 นาที

แชร์บทความ

CORS ไม่ใช่กำแพงกันแฮกเกอร์: วิธีตั้งค่าให้ปลอดภัยและไม่เปิดอ่านข้อมูล

CORS ไม่ใช่กำแพงกันแฮกเกอร์: วิธีตั้งค่าให้ปลอดภัยและไม่เปิดอ่านข้อมูล

หลายคนมักเข้าใจว่า CORS เป็นเหมือนกำแพงที่ช่วยกันแฮกเกอร์ไม่ให้เข้าถึง API แต่ในความเป็นจริง CORS คือกติกาที่ server ประกาศให้ browser รับรู้ว่า เว็บไซต์ใดมีสิทธิ์อ่าน response จาก API ของเราได้บ้าง

ประเด็นสำคัญคือ CORS ไม่ได้หยุดทุก request ที่เข้ามาหา API แต่ควบคุมว่า response จะถูกเปิดให้อ่านได้หรือไม่ หากตั้งค่าผิด โดยเฉพาะกับ endpoint ที่มีข้อมูลสำคัญ ก็อาจกลายเป็นช่องโหว่ร้ายแรงได้

CORS ทำหน้าที่อะไร และไม่ได้ทำหน้าที่อะไร

CORS หรือ Cross-Origin Resource Sharing เป็นกลไกที่เบราว์เซอร์ใช้ตัดสินใจว่า JavaScript จาก origin หนึ่ง จะสามารถอ่านผลลัพธ์จากอีก origin หนึ่งได้หรือไม่

กล่าวให้ชัดขึ้นคือ

  • CORS ไม่ได้เป็นไฟร์วอลล์
  • CORS ไม่ได้ป้องกันไม่ให้ attacker ส่ง request ได้เสมอไป
  • CORS ควบคุมเรื่อง การอ่าน response ผ่าน browser เป็นหลัก

ดังนั้น ความเข้าใจที่ถูกต้องคือ ปัญหาหลักของ CORS misconfiguration ไม่ใช่แค่ “มีคนยิง request มาหา API” แต่คือ “เว็บที่ไม่ควรอ่านข้อมูล กลับอ่าน response ได้”

จุดเริ่มต้นของความเสี่ยง: อนุญาต origin กว้างเกินไป

ความผิดพลาดที่พบบ่อยคือ server ตอบกลับแบบกว้างเกินจำเป็น เช่น

Access-Control-Allow-Origin: *

หรือร้ายกว่านั้นคือสะท้อนค่า Origin ที่ผู้ร้องขอส่งมาโดยไม่ตรวจสอบจริง เช่น

  • ผู้โจมตีส่ง Origin: https://evil.example
  • server ตอบ Access-Control-Allow-Origin: https://evil.example

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

อย่างไรก็ตาม การใช้ * ไม่ได้ผิดเสมอไป หากข้อมูลนั้นเป็น public จริง ๆ เช่น

  • ไฟล์ config ที่เปิดเผยได้
  • รูปภาพสาธารณะ
  • endpoint ที่ไม่มีข้อมูลเฉพาะผู้ใช้

แต่ถ้าเป็น API ที่เกี่ยวข้องกับข้อมูลส่วนตัวหรือข้อมูลสำคัญ เช่น

  • /me
  • /order-history
  • /billing
  • /admin/profile

ควรระวังเป็นพิเศษ เพราะ browser ของเหยื่ออาจแนบ cookie หรือ token ไปกับ request โดยอัตโนมัติ

คู่ผสมที่อันตราย: Credentials + Origin ที่ปล่อยหลวม

สถานการณ์ที่อันตรายที่สุดมักเกิดเมื่อมีสองสิ่งนี้พร้อมกัน

Access-Control-Allow-Credentials: true

และมีการอนุญาต origin แบบกว้าง หรือ validate แบบหลวมเกินไป

ผลคือ หากเหยื่อกำลัง login ค้างอยู่ และเข้าเว็บไซต์โจมตี JavaScript บนเว็บโจมตีอาจส่งคำขอไปยัง API ของเราได้ เบราว์เซอร์ก็จะแนบ cookie ให้ตามปกติ และถ้า CORS อนุญาต origin นั้น เว็บโจมตีก็สามารถอ่าน response กลับไปได้ทันที

นี่คือเส้นแบ่งสำคัญของความเสียหาย เช่น

  • อ่านข้อมูล profile ได้
  • อ่านประวัติคำสั่งซื้อได้
  • อ่านข้อมูล billing ได้
  • อ่านผลลัพธ์หลังเปลี่ยนค่าบางอย่างได้
  • ในบางระบบอาจอ่าน token หรือข้อมูลแอดมินได้

เพราะฉะนั้น CORS ที่เปิดกว้างเมื่อใช้ร่วมกับ credentials จึงไม่ใช่แค่การตั้งค่าผิดเล็กน้อย แต่สามารถกลายเป็น incident ได้จริง

รูปแบบการตรวจ origin ที่พลาดบ่อย

ความผิดพลาดที่เจอบ่อยมากคือการตรวจสอบ origin แบบไม่ strict พอ เช่นตรวจแค่ว่ามีคำว่า example.com อยู่ในสตริงก็ถือว่าผ่าน

ตัวอย่างที่อันตราย ได้แก่

  • https://evil-example.com
  • https://example.com.evil.com
  • https://example.com.attacker.net

ทั้งหมดนี้อาจผ่านการตรวจแบบหลวม ๆ ได้ ทั้งที่ไม่ใช่โดเมนที่ควรอนุญาต

อีกตัวอย่างคือการใช้ regex ที่ไม่ anchor เช่น

/.*example.com.*/

แม้ดูเหมือนใช้งานได้ แต่จริง ๆ แล้วหลุดง่ายมาก เพราะมันยอมรับทุกค่า ที่มีข้อความ example.com แทรกอยู่ตรงไหนก็ได้

แนวทางที่ควรหลีกเลี่ยง ได้แก่

  • contains
  • startsWith แบบไม่รัดกุม
  • regex ที่ไม่ anchor ต้นและท้าย
  • การ auto-allow ทุก subdomain โดยไม่มีเหตุผลทางธุรกิจชัดเจน

อย่ามองข้าม null origin

อีกกรณีหนึ่งที่อันตรายคือการเชื่อ null origin เช่น

Access-Control-Allow-Origin: null

หลายคนอาจคิดว่า null หมายถึงไม่มีที่มา จึงไม่น่ามีปัญหา แต่ในบริบทของ security มันมักแปลว่า “ต้องคิดต่อ” มากกว่า “ปลอดภัย”

null origin อาจเกิดจากหลายกรณี เช่น

  • local file
  • sandboxed iframe
  • data URL
  • execution context แปลก ๆ อื่น ๆ

หาก API สำคัญและยอม null ผู้โจมตีอาจพยายามสร้าง context พิเศษเพื่อให้ browser อ่านข้อมูลจาก API ได้

วิธีตรวจ CORS แบบ practical

การตรวจสอบเบื้องต้นสามารถทำได้ง่าย ๆ ด้วย DevTools หรือ curl โดยส่ง Origin แปลก ๆ เข้าไปแล้วดูว่า server ตอบอะไรกลับมา

ตัวอย่างแนวคิดในการทดสอบคือ

  • ส่ง Origin: https://attacker.test
  • ถ้า server สะท้อนค่านี้กลับมาใน Access-Control-Allow-Origin
  • ต้องถามต่อทันทีว่า endpoint นี้มีข้อมูลสำคัญหรือไม่
  • หากมี Access-Control-Allow-Credentials: true ด้วย ให้ถือว่าเป็นสัญญาณอันตรายรุนแรง

payload ที่ควรลองทดสอบมีหลายรูปแบบ เช่น

  • https://evil.com
  • https://example.com.evil.com
  • https://evil-example.com
  • null
  • http://trusted.com
  • https://trusted.com:444

เหตุผลคือหลายระบบพลาดเรื่องเหล่านี้บ่อย

  • scheme: http ไม่เท่ากับ https
  • port: 443 ไม่เท่ากับ 444
  • subdomain: trusted.com ไม่เท่ากับ api.trusted.com

การเทียบ origin ที่ถูกต้องจึงต้องมองทั้ง scheme, host และ port ร่วมกัน

วิธีแก้ที่ถูกต้อง: ใช้ allowlist แบบ exact match

แนวทางที่ดีคือเก็บรายการ origin ที่อนุญาตจริงไว้ใน allowlist แล้วเทียบแบบ exact match เท่านั้น เช่น

  • https://app.example.com
  • https://admin.example.com

หลักการสำคัญคือ

  • อนุญาตเฉพาะ origin ที่จำเป็นต่อธุรกิจจริง ๆ
  • ไม่ใช้ contains
  • ไม่ใช้ startsWith แบบคร่าว ๆ
  • ไม่เปิดทุก subdomain ถ้าไม่จำเป็น
  • ถ้า response เปลี่ยนตาม origin ควรส่ง Vary: Origin
  • เขียน automated test เพื่อกัน regression

หากเป็น private API ควรหลีกเลี่ยง Access-Control-Allow-Origin: * โดยเด็ดขาด โดยเฉพาะเมื่อ endpoint มีข้อมูลของผู้ใช้

กรณีหลาย tenant ต้องระวังมากเป็นพิเศษ

ระบบแบบ multi-tenant มักมีความเสี่ยงเพิ่มขึ้น หากเปิดให้ tenant ตั้งค่า CORS เองแล้วชี้ origin ไปยังโดเมนใดก็ได้โดยไม่ verify ownership

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

แนวทางที่ควรทำคือมีขั้นตอนพิสูจน์ความเป็นเจ้าของโดเมนก่อน เช่น

  • ยืนยันผ่าน DNS record
  • ยืนยันผ่านไฟล์บนเว็บ
  • ใช้วิธี verify ownership แบบอื่นที่ตรวจสอบได้จริง

สิ่งนี้ช่วยลดความเสี่ยงจากการที่ผู้ใช้รายหนึ่งตั้งค่าระบบในแบบที่กระทบผู้ใช้รายอื่น

หลักคิดสั้น ๆ สำหรับการตั้งค่า CORS ให้ปลอดภัย

สรุปแนวทางที่ควรจำมีดังนี้

  • public API ใช้ * ได้ ถ้าข้อมูลเป็น public จริง
  • private API ห้ามใช้ *
  • Access-Control-Allow-Credentials: true ต้องใช้คู่กับ origin ที่ล็อกแน่นเท่านั้น
  • อย่าตรวจ origin แบบหลวม ๆ
  • ระวัง null origin
  • ส่ง Vary: Origin เมื่อ response เปลี่ยนตาม origin
  • เขียน test เพื่อป้องกันการตั้งค่าถอยหลังโดยไม่ตั้งใจ

สรุป

CORS ที่ดีไม่ใช่การเปิดให้ frontend ทำงานง่ายที่สุด แต่คือการเปิดเท่าที่จำเป็นต่อธุรกิจจริง ๆ เท่านั้น ยิ่งอนุญาต origin กว้างมากเท่าไร ก็ยิ่งต้องมั่นใจมากขึ้นว่าไม่มีข้อมูลที่ไม่ควรถูกอ่านอยู่เบื้องหลัง

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

แหล่งอ้างอิง