CORS คืออะไร?

demystify-cors

CORS คืออะไร?

CORS (Cross-Origin Resource Sharing) เป็นเมคานิกส์ด้านความปลอดภัยของเว็บบราวเซอร์ของเราที่จะไม่อนุญาตให้มีการเรียกหาข้อมูลข้ามโดเมนกัน

ข้าม = Cross

โดเมน = Origin

ท่าประจำในการพัฒนาเว็บที่พบเจอได้บ่อยก็คือ แยกส่วนการแสดงผล frontent กับจัดการฐานข้อมูล และส่วนหลังบ้าน หรือ backend ออกจากกัน, แต่ปัญหาแรกที่พบก็คือ เวลาที่พัฒนา frontend และ backend เสร็จแล้ว จะให้มาทดสอบในการเรียกข้อมูลดู ก็พบว่า Error และเรียกไปยัง API ที่กำหนดไว้ไม่ได้ ทั้งๆ ที่เวลาทดสอบด้วย Postman ก็ยังทดสอบเรียกดูข้อมูลได้ถูกต้องอยู่เลย

cors-error-chrome

สาเหตุก็อย่างที่นิยามของ CORS ด้านบน บราวเซอร์ป้องกันการเรียกขอข้ามโดเมน หรือ Origin แม้กระทั่งเครื่องที่เราใช้ทดสอบในการพัฒนา จะเป็นเครื่องเดียวกัน แต่บราวเซอร์จะมองว่าเป็นต่างโดเมนกัน

นี่ก็เมคานิกส์เพื่อป้องกันการใช้งานได้อย่างปลอดภัย ป้องกันการโจมตีไป backend เป็นต้น

ยกตัวอย่าง

  # Local Environment
  http://localhost:5173/ -> http://localhost:8080/api/delivery/status/SM9389238128JP

  # Real-World Case
  https://good-ecommerce.com -> https://api.good-product.com/products/BIKE82912AMZ

วิธีการ Implementation

เราต้องไปทำการตั้งค่าให้ backend เป็นฝ่ายรับผิดชอบในการดูว่า request นั้นได้มาจากโดเมน หรือ Origin ที่ได้รับอนุญาตหรือไม่ แล้วก็ส่ง response ที่มี header ว่าอนุญาตให้ origin ไหน เรียก endpoint แบบใดได้บ้าง

# Example Allow Only One
Access-Control-Allow-Origin: http://www.example.com

# Example Allow All
# ❌ **ระวัง ค่านี้เพื่อการยกตัวอย่างประกอบ blog เท่านั้น อย่าหาทำบน Production เด็ดขาด**
Access-Control-Allow-Origin: *

Tech Stack

เพื่อการยกตัวอย่าง หาผู้อ่านถนัดภาษา และเฟรมเวิร์กอื่น ก็สามารถหา documentation ของ tool นั้นๆ เพื่อไปประยุกต์กันเอาเองนะ

  • 🌱👢 Spring Boot 4
  • 🛠️⚡ SvelteKit 2 & Svelte 5
  • Java 25 JDK (GraalVM for native build)
  • NodeJS
  • PNPM

Project Setup

shamuneko-delivery

  1. ติดตั้งโปรแกรมที่จำเป็น

    • JDK 25
    • NodeJS 22+
    • PNPM for Node Package Management
  2. Checkout จาก repository

    https://github.com/santa-sandbox/demystify-cors
    • spring-boot สำหรับ backend
    • svelte สำหรับ frontend
  3. สั่งรัน backend

    1. cd spring-boot
    2. ./mvnw spring-boot:run เพื่อสั่งรัน backend
  4. สั่งรัน frontend

    1. cd svelte
    2. pnpm install รันคำสั่งก่อนครั้งแรกครั้งเดียว
    3. pnpm run dev เพื่อสั่งรัน frontend
  5. เปิดเว็บบราวเซอร์ เพื่อเช็ค

    1. http://localhost:5173
    2. ลองใส่รหัส Tracking พัสดุสมมติ SM9389238128JP แล้วกด Track Package
    3. จะเจอ error ตามภาพด้านล่าง

chrome-cors-global-config

  1. เปิด Chrome DevTools
    1. ไปดูที่ Console แล้วดูว่ามี error อะไรบ้าง

CORS Configuration

โค้ดตัวอย่าง config แบบ Global

cors-config

// โค้ดตัวอย่าง
blog.natta.santa.cors.config.CorsConfig.java
  1. จากโค้ดตัวอย่าง จะเห็นว่าเป็น Code ที่ถูก comment เอาไว้ ก็เอา comment ออก, รีสตาร์ท backend
  2. เปิดดู frontend หรือ refresh อีกที
  3. ใส่รหัส Tracking พัสดุสมมติ SM9389238128JP แล้วกด Track Package อีกรอบ จะเห็นว่า error เปลี่ยนไปแล้ว /api/v1/delivery/status/SM9389238128JP จะไม่ขึ้นมาแล้ว แต่จะติดที่ /api/v2/receipts/SM9389238128JP แทน

chrome-cors-global-succeed

โค้ดตัวอย่าง config แบบ @CrossOrigin Annotation

cors-cross-origin-annotation

  1. ในโค้ดตัวอย่าง มองหา @CrossOrigin แล้วเอา comment ออก
  2. ทำซ้ำ
    1. restart backend
    2. refresh frontend
    3. ใส่รหัส Tracking พัสดุสมมติ SM9389238128JP แล้วกด Track Package

chrome-cors-cross-origin-succeed

Conclusion

CORS คือเมคานิกส์ป้องกันการ request ข้ามโดเมน ซึ่งเป็น ฟีเจอร์ของ browser ในยุคปัจจุบัน แต่เวลาเราต้องการจะเปิดรับ request ข้ามโดเมน ต้องมีการตั้งค่าให้ฝั่ง backend ตามแนวคิด Principle of Least Privilege (PoLP) ที่จะเปิดรับจาก client ที่เราอนุญาตเท่านั้น ซึ่งจะช่วยลดการพยายามโจมตีมาที่เซอร์เวอร์ของเราได้

References