ข้าม​ไป​ยัง​เนื้อหา

สอง​กลไก​ที่​ไม่​ทับ​กัน CORS ปิด​ช่อง​ที่ CSRF ปล่อย​ผ่าน และ SameSite ที่​ไม่มี​ค่า​ตั้ง​ต้น

เจ็ด​บท​ก่อน​หน้า​วัด​สอง​กลไก​นี้​ที​ละ​ตัว บท​นี้​วาง​มัน​ซ้อน​กัน​แล้ว​ถาม​ข้อ​เดียว คือ​สอง​กลไก​นี้​ทับ​กัน หรือ​ปิด​คนละ​รู

คำ​ตอบ​ที่​วัด​ได้​มี​สอง​ครึ่ง ครึ่ง​แรก​คือ​มัน​แยก​กัน​จริง ครึ่ง​ที่​สอง​คือ​รู​ที่​กลไก​หนึ่ง​เปิด​ทิ้ง​ไว้ ถูก​ปิด​ด้วย​อีก​กลไก​หนึ่ง และ​ไม่ใช่​ทิศ​ที่​สัญชาตญาณ​บอก

📦 บท​นี้​ไม่​ได้​สร้าง​ทรง​ใหม่ มัน​วาง​ทรง​เก่า​คู่​กัน

ทรง​ที่​บท​นี้​เทียบ​มี​อยู่​แล้ว​ใน probe ทั้งหมด สิ่ง​ที่​บท​นี้​ทำ​คือ​อ่าน​สอง​ทรง​พร้อม​กัน ที​ละ​ช่อง แล้ว​ดู​ว่า​ช่อง​ไหน​ขยับ

ตัวเลข​ทุก​ตัว​รัน​เอง​ได้ สั่ง scripts/clinic-cross-origin/up.sh แล้ว​ตาม​ด้วย python3 scripts/clinic-cross-origin/probe.py

ทรง​คู่​ที่​บท​นี้​อ่าน ต่าง​กัน​ที่ bean ไม่ใช่​ที่​กฎ​การ​อนุญาต

หัวข้อ​ที่​มีชื่อ​ว่า “ทรง​คู่​ที่​บท​นี้​อ่าน ต่าง​กัน​ที่ bean ไม่ใช่​ที่​กฎ​การ​อนุญาต”

csrf-on-cors คือ​ทรง csrf-on ที่​เติม bean ของ CORS เข้า​มา และ​เติม​หนึ่ง​บรรทัด​ใน SecConfig.kt

CSRFCross-Site Request Forgery (CSRF)การ​ที่​หน้า​เว็บ​ของ​ผู้​อื่น​สั่ง​เบราว์เซอร์​ของ​ผู้​ใช้​ให้​ยิง​คำขอ​ไป​ยัง​แอป​ที่​ผู้​ใช้​ยัง login ค้าง​อยู่ · กลไก​กัน​ของ Spring Security คือ​ค่าที่​ผู้​เรียก​ต้อง​แนบ​มา​เอง ซึ่ง​หน้า​เว็บ​ของ​ผู้​อื่น​อ่าน​ไม่​ได้ คือ​กลไก​ที่​ถาม​ว่า​คำขอ​ใบ​นี้​มา​จาก​หน้า​เว็บ​ของ​แอป​เอง​หรือ​ไม่ ส่วน CORSCross-Origin Resource Sharing (CORS)ชุด​กฎ​ที่​บอก​เบราว์เซอร์​ว่า​หน้า​เว็บ​จาก​ต้นทาง​หนึ่ง อ่าน​คำ​ตอบ​จาก​อีก​ต้นทาง​ได้​หรือ​ไม่ · บังคับ​ที่​เบราว์เซอร์​เป็น​หลัก จึง​กัน​การ​อ่าน ไม่​ได้​กัน​การ​เขียน​โดย​ตัว​มัน​เอง คือ​ชุด​กฎ​ที่​บอก​เบราว์เซอร์​ว่า​หน้า​เว็บ​จาก​ต้นทาง​หนึ่ง อ่าน​คำ​ตอบ​จาก​อีก​ต้นทาง​ได้​ไหม

สอง​ทรง​ที่​บท​นี้​เทียบ​คือ csrf-on กับ csrf-on-cors ความ​ต่าง​ทั้งหมด​ของ​มัน​มี​สอง​อย่าง

  • csrf-on-cors มี CorsConfig.kt เพิ่ม​เข้า​มา​อีก​หนึ่ง​ตัว ซึ่ง​อนุญาต​ต้นทาง https://allowed.example ตัว​เดียว
  • SecConfig.kt ของ​มัน​มี​บรรทัด http.cors { } เพิ่ม​มา​อีก​หนึ่ง​บรรทัด

กฎใน authorizeHttpRequests เหมือน​กัน​ทั้ง​สอง​ทรง formLogin เหมือน​กัน ผู้​ใช้​คน​เดียวกัน และ​ปลาย​ทาง​ทุก​ตัว​เหมือน​กัน​ทุก byte ช่อง​ที่​ขยับ​จึง​ขยับ​เพราะ​สอง​อย่าง​นั้น

คำขอ​ที่​กลไก​กัน​ปลอม​ตัดสิน ได้​คำ​ตัดสิน​เดิม​ทั้ง​ทรง​ที่​ไม่มี CORS และ​ทรง​ที่​มี

ทุก​คำขอ​ใน​ตาราง​นี้​พก cookie ของ session ที่ login สำเร็จ​แล้ว และ​ไม่​ประกาศ Origin ช่อง​ซ้าย​คือ​สิ่ง​ที่​บท​ที่ 1 วัด​ไว้ ช่อง​ขวา​คือ​สิ่ง​ที่​บท​นี้​เติม​เข้า​มา

POST /visitsทรง csrf-onทรง csrf-on-cors
ไม่มี CSRF token403 · +0 แถว403 · +0 แถว
ส่ง CSRF token มา​ทาง header200 · +1 แถว200 · +1 แถว

สอง​แถว​นี้​ไม่มี​ช่อง​ไหน​ต่าง​กัน​เลย การ​เติม bean ของ CORS เข้า​มา ไม่​ได้​ขยับ​คำ​ตัดสิน ของ​กลไก​กัน​ปลอม​สัก​ช่อง​เดียว

นี่​คือ​ความ​หมาย​ของ​คำ​ว่า​แยก​กัน กลไก​หนึ่ง​ถาม​ว่า CSRF token มา​ถึง​หรือ​ไม่ อีก​กลไก​ถาม​ว่า​ต้นทาง​ที่​ประกาศ​มา​อยู่​ใน​ราย​ชื่อ​ที่​อนุญาต​หรือ​ไม่ และ​ไม่มี​ตัว​ไหน​ตอบแทน​กัน​ได้

probe ยัง​ยิง​อีก​ใบ​เพื่อ​ปิด​ข้อ​สงสัย​ที่​เหลือ คือ POST /visits ที่​ประกาศ​ต้นทาง https://allowed.example และ​แนบ CSRF token มา​ครบ ยิง​ใส่​ทรง csrf-on-cors ผล​คือ 200 และ​แถว​เพิ่ม 1 แถว ผ่าน​ทั้ง​สอง​กลไก​พร้อม​กัน

ช่อง​ที่​กลไก​กัน​ปลอม​ไม่​ได้​ปิด และ​ตัว​ที่​ปิด​มัน​จริง

หัวข้อ​ที่​มีชื่อ​ว่า “ช่อง​ที่​กลไก​กัน​ปลอม​ไม่​ได้​ปิด และ​ตัว​ที่​ปิด​มัน​จริง”

GET /visits/touch เขียน​ฐาน​ข้อมูล และ​มัน​เดิน​ผ่าน​กลไก​กัน​ปลอม​ไป​ได้​ทั้ง​ดุ้น

บท​ที่ 4 วัด​ปลาย​ทาง​ตัว​นี้​ไว้​แล้ว มัน​เป็น safe methodsafe methodmethod ที่​สเปก​จัด​ว่า​ไม่​เปลี่ยน​สถานะ เช่น GET · กลไก​กัน​ปลอม​จึง​ปล่อย​ผ่าน วัด​แล้ว​ว่า​ทรง csrf-on ให้ GET ที่​เขียน​ฐาน​ข้อมูล​จริง​ได้ 200 และ​แถว​เพิ่ม​หนึ่ง​แถว ทั้ง​ที่​ยิง​มา​จาก​ต้นทาง​ของ​ผู้​โจมตี ใน​สายตา​ของ​สเปก จึง​ไม่​ถูก​ตรวจ CSRF token ทั้ง​ที่​ตัว​มัน​เขียน​หนึ่ง​แถว​ลง​ตาราง csrf_visit จริง

คำขอ​ที่​ยิง​คือ​ใบ​เดียวกัน​ทั้ง​สอง​ช่อง คือ GET /visits/touch ที่​ประกาศ Origin: https://evil.example และ Referer: https://evil.example/attack.html

GET /visits/touch จาก​ต้นทาง​ของ​ผู้​โจมตีรหัสแถว​ใน csrf_visit
ทรง csrf-on — สิ่ง​ที่​บท​ที่ 4 วัด​ไว้200+1
ทรง csrf-on-cors403+0

body ของ 403 ใบ​นั้น​ไม่ใช่ body ของ​กลไก​กัน​ปลอม มัน​เป็น​ข้อความ​ปฏิเสธ​ของ CORS ซึ่ง​บท​ที่ 7 อ่าน​ตัว​อักษร​ของ​มัน​ไว้​แล้ว ผู้​ปฏิเสธ​จึง​เป็น​คนละ​ตัว​กับ​ที่​บท​ที่ 1 วัด

POST /visits ที่​แนบ CSRF token ครบ แต่​ประกาศ​ต้นทาง​ของ​ผู้​โจมตี ให้​ผล​รูป​เดียวกัน คือ​ทรง csrf-on แถว​เพิ่ม 1 แถว ส่วน​ทรง csrf-on-cors ตอบ 403 และ​ไม่มี​แถว​ลง

สัญชาตญาณ​บอก​ว่า​กลไก​กัน​ปลอม​ดูแล​การ​เขียน และ CORS เป็น​เรื่อง​ของ​การ​อ่าน ที่​วัด​ได้​คือ​กลับ​กัน​ใน​ช่อง​นี้ การ​เขียน​ที่​หลุด​ออก​ไป ถูก​ปิด​ด้วย​การ​ตั้ง​ค่า CORS

คำขอ​ใบ​เดียว​ถูก​ถาม​สอง​คำถาม และ​คำถาม​ที่​ปฏิเสธ​มัน​ขึ้น​กับ​ว่า​ใบ​นั้น​มี​รูป​แบบ​ไหน

flowchart TB
  A["คำขอหนึ่งใบ · พก cookie ของ session ที่ login แล้ว"] --> Q1{"เป็น POST ที่ไม่มี CSRF token ไหม"}
  Q1 -->|"ใช่"| R1["กลไกกันปลอมปฏิเสธ · แถวไม่ลง · ผลเท่ากันทั้งทรง csrf-on และ csrf-on-cors"]
  Q1 -->|"ไม่ใช่ · เป็น GET ที่เขียน หรือ POST ที่แนบ CSRF token ครบ"| Q2{"ทรงนี้มี bean ของ CORS ไหม"}
  Q2 -->|"csrf-on · ไม่มี"| R2["ผ่าน · แถวลง"]
  Q2 -->|"csrf-on-cors · มี และประกาศต้นทางที่ไม่อยู่ในรายชื่อ"| R3["403 · แถวไม่ลง"]
  Q2 -->|"csrf-on-cors · มี และประกาศต้นทางที่อยู่ในรายชื่อ หรือไม่ประกาศต้นทางเลย"| R4["200 · แถวลง"]

คำ​บรรยาย​ภาพ: คำถาม​แรก​เป็น​ของ​กลไก​กัน​ปลอม และ​คำ​ตอบ​ของ​มัน​ไม่​ขึ้น​กับ CORS เลย คำถาม​ที่​สอง​เป็น​ของ CORS และ​มัน​ได้​ตัดสิน​ก็​ต่อ​เมื่อ​คำถาม​แรก​ปล่อย​ผ่าน​มา​แล้ว

การ​ตั้ง​ค่า​ที่​ส​ตาร์ต​ได้ แล้ว​พัง​ตอน​มี​ต้นทาง​เข้า​มา

หัวข้อ​ที่​มีชื่อ​ว่า “การ​ตั้ง​ค่า​ที่​ส​ตาร์ต​ได้ แล้ว​พัง​ตอน​มี​ต้นทาง​เข้า​มา”

allowedOrigins("*") คู่​กับ allowCredentials = true เป็น​คู่​ที่​ผิด​กฎ และ​แอป​ก็​ยัง​ส​ตาร์ต​ขึ้น​มา

CorsConfig.kt ของ​ทรง cors-star-credentials
package clinic.csrf
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
@Configuration
class CorsConfig {
@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val c = CorsConfiguration()
c.allowedOrigins = listOf("*")
c.allowedMethods = listOf("GET", "POST", "OPTIONS")
c.allowedHeaders = listOf("*")
c.allowCredentials = true
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", c)
return source
}
}

ทรง​นี้​ปิด​กลไก​กัน​ปลอม​ไว้ ไม่​เหมือน​สอง​ทรง​ใน​หัวข้อ​ก่อน​หน้า POST /visits ที่​ไม่มี CSRF token จึง​ไม่มี​ใคร​ตรวจ​มัน​ตั้งแต่​ต้น ทรง​ที่​เทียบ​ได้​ตรง​ตัว​จึง​เป็น cors-configured ซึ่ง​ปิด​กลไก​กัน​ปลอม เหมือน​กัน​และ​อนุญาต​ต้นทาง​เดียว สอง​ทรง​นั้น​ต่าง​กัน​ตรง​บรรทัด c.allowedOrigins บรรทัด​เดียว ที่​เหลือ​เท่า​กัน​ทุก byte

ช่อง​ที่​วัดผล​ของ​ทรง cors-star-credentials
แอป​ส​ตาร์ต​ขึ้น​ไหมส​ตาร์ต​ได้
POST /visits ที่​ไม่​ประกาศ Origin200 · +1 แถว
POST /visits ที่​ประกาศ Origin ของ​ผู้​โจมตี500 · +0 แถว
Access-Control-Allow-* ใน​คำ​ตอบ​ใบ​ที่​พัง0 ตัว
exception ที่​โผล่​ใน logjava.lang.IllegalArgumentException

นี่​คือ​การ​ตั้ง​ค่าที่​มอง​ไม่​เห็น​ได้​ง่าย​ที่สุด​ใน​คอร์ส​นี้ ชุด​เทสต์​ที่​ยิง​จาก​ใน​เครื่อง​เดียวกัน ไม่​ประกาศ Origin มา​ด้วย มัน​จึง​ตกลง​ใน​แถว​ที่​สอง​ของ​ตาราง และ​เขียว​สนิท

รหัส​ที่​ออก​มา​ไม่ใช่ 403 ที่​แปล​ว่า​ปฏิเสธ มัน​เป็น 500 ที่​แปล​ว่า​แอป​โยน exception ออก​มา ผู้​เรียก​ที่​เห็น​รหัส​นี้​จึง​อ่าน​ไม่​ออก​ว่า​ตัว​เอง​ถูก​ปฏิเสธ​ด้วย​กฎ หรือ​แอป​พัง

ตัวเลข​ทุก​ตัว​ผูก​กับ​รุ่น​ที่​ระบุ​ไว้

ผล​ทั้ง​บท​วัด​บน Spring Boot 4.1.1 · Kotlin 2.4.10 · JDK 25 รุ่น​อื่น​เป็น​คนละ​การ​วัด

โดย​เฉพาะ​ข้อ​ที่​ว่า​แอป ส​ตาร์ต​ได้ ทั้ง​ที่​คู่​ค่า​นี้​ผิด​กฎ ข้อ​นี้​เป็น​ผล​ของ​ทรง​ที่ probe สร้าง​ขึ้น​บน​รุ่น​นั้น ไม่ใช่​กฎ​ของ Spring ที่​อ่าน​ได้​จาก​ที่​อื่น

วัด​ได้​ว่า Boot 4 ไม่​ใส่​ค่า​ตั้ง​ต้น​มา​ให้ และ property หนึ่ง​บรรทัด​เขียน​ค่า​นั้น​ลง​ไป​ตรง​ตัว

SameSiteSameSiteคุณสมบัติ​ของ cookie ที่​บอก​เบราว์เซอร์​ว่า​จะ​แนบ cookie ไป​กับ​คำขอ​ข้าม​ไซต์​หรือ​ไม่ · คอร์ส​นี้​อ้าง​ได้​แค่ literal ที่ server คาย​ออก​มา ซึ่ง​วัด​แล้ว​ว่า Boot 4 ไม่​ใส่​ค่า​ตั้ง​ต้น​มา​ให้ และ​ค่า​นั้น​ไม่​เปลี่ยน​จำนวน​แถว​สัก​แถว คือ​คุณสมบัติ​ของ cookie ที่​บอก​เบราว์เซอร์​ว่า​จะ​แนบ cookie ไป​กับ​คำขอ​ข้าม​ไซต์​หรือ​ไม่ สิ่ง​ที่​บท​นี้​วัด​คือ​ตัว​อักษร​ใน Set-Cookie ที่ server ส่ง​ออก​มา

ทรง samesite-lax กับ samesite-none คือ​ทรง csrf-off บวก property หนึ่ง​บรรทัด

application.properties ของ​ทรง samesite-lax · บรรทัด​เดียว​ที่​ทรง​นี้​เพิ่ม​เข้า​มา
server.servlet.session.cookie.same-site=Lax

ทรง samesite-none คือ​บรรทัด​เดียวกัน​นี้ ที่​มี​ค่า​เป็น None แทน Lax ไม่มี byte อื่น​ต่าง​กัน

ทรงSet-Cookie ที่​คำ​ตอบ​ของ /login ส่ง​กลับ​มา
csrf-offJSESSIONID=<id>; Path=/; HttpOnly
samesite-laxJSESSIONID=<id>; Path=/; HttpOnly; SameSite=Lax
samesite-noneJSESSIONID=<id>; Path=/; HttpOnly; SameSite=None

<id> คือ​ค่าที่ probe แทนที่​ไว้​เอง เพราะ​มัน​เปลี่ยน​ทุกร​อบ ส่วน​ตัว​อักษร​ที่​เหลือ​เป็น​ของ​จริง แถว​แรก​จึง​เป็น​ข้อ​อ้าง​ข้อ​หนึ่ง คือ ไม่มี SameSite โผล่​ออก​มา​เลย​เมื่อ​ไม่​ได้​ตั้ง​ค่า

ทั้ง​สอง​ทรง​ที่​ตั้ง​ค่า​ไว้ ยัง​รับ POST /visits แล้ว​เขียน 1 แถว เท่า​กัน property ตัว​นี้​จึง​เขียน​ตัว​อักษร​ลง​ใน header และ​ไม่​ได้​ห้าม​อะไร​ที่​ฝั่ง server เลย

cookie ใบ​ที่​ตาราง​นี้​อ่าน คือ​ใบ​ที่ /login คาย​ออก​มา และ​ใน​ทรง​ที่​กลไก​กัน​ปลอม​ปิด​อยู่ ยัง​ไม่มี session อยู่​ก่อน​หน้า​นั้น​เลย ส่วน​ทรง csrf-on มี session อยู่​ก่อน login แล้ว ซึ่ง​เป็น​ข้อ​ที่​บท​ที่ 2 วัด​ไว้​ใน​ตาราง​ของ​ทรง​นั้น

สิ่ง​ที่​คอร์ส​นี้​อ้าง​เรื่อง SameSite ไม่​ได้

พฤติกรรม​ข้าม​ไซต์​ของ SameSite ยัง​ไม่​ได้​วัด และ​คอร์ส​นี้​จะ​ไม่​อ้าง​มัน

ชุด​วัด​ฝั่ง​เบราว์เซอร์​ไม่​เคย​ตั้ง cookie ให้​ต้นทาง​ของ API เลย ช่อง cookieSeen จึง​เป็น false ทุก​ทรง และ SameSite=None ต้อง​มา​คู่​กับ Secure ซึ่ง http ธรรมดา​ให้​ไม่​ได้

ที่​คอร์ส​นี้​อ้าง​ได้​คือ​ตัว​อักษร​ที่ server คาย​ออก​มา และ​จำนวน​แถว​ที่​ไม่​ขยับ ไม่ใช่​สิ่ง​ที่​เบราว์เซอร์ ตัดสิน​ใจ ทำ​กับ​ตัว​อักษร​นั้น

คำถาม​ตั้ง​ต้น​ได้​คำ​ตอบ​เป็น​จำนวน​แถว และ​รายการ​ที่​ไม่​ได้​ตอบ​ก็​ยาว​พอกัน

ก่อน​คอร์ส​นี้ นับ​เมื่อ 2026-09-05 ทั้ง​ไซต์​เอ่ย​ถึง​สอง​เรื่อง​นี้​รวม​กัน 2 บรรทัด หนึ่ง​ใน​นั้น​คือ​บรรทัด​ที่​คอร์ส​ที่ 35 ปิด​กลไก​กัน​ปลอม​ทิ้ง และ​อีก​บรรทัด​คือ​ประโยค​ที่​บอก​ว่า เรื่อง​นี้​ใหญ่​เกิน​กว่า​แปดบท​ของ​คอร์ส​นั้น​จะ​รับ​ไหว

สิ่ง​ที่​แปดบท​นี้​ตอบ​ไป​แล้ว

  • บรรทัด​ที่​ถูก​ปิด ปิด​คำถาม​ข้อ​ที่​สอง​ไป คือ​คำขอ​ใบ​นี้​มา​จาก​หน้า​เว็บ​ของ​แอป​เอง​หรือ​ไม่
  • CSRF token ผูก​กับ session หนึ่ง​ใบ และ​มัน​ไม่ใช่​ค่าที่​ใช้ได้​ครั้ง​เดียว
  • คำขอ​รูป​เดียวกัน​ได้​รหัส​คนละ​ตัว เพราะ​ลำดับ​ของ filter ไม่ใช่​เพราะ​ค่า​ใน config
  • GET ที่​เขียน​ฐาน​ข้อมูล เดิน​ผ่าน​กลไก​กัน​ปลอม​ไป​ได้
  • เบราว์เซอร์​บอก​ว่า​บล็อก แต่​แถว​ลง​ไป​แล้ว เพราะ​การ​ปฏิเสธ​เกิด​ที่​ขั้น​อ่าน​คำ​ตอบ
  • มี​ทรง​ที่​การ​เขียน​ไม่​เคย​เกิด​ขึ้น​เลย และ​เส้น​แบ่ง​อยู่​ที่​รูป​ของ​คำขอ
  • ตั้ง​ค่า CORS แล้ว server ปฏิเสธ​เอง และ​ตัว​สวิตช์​คือ bean ไม่ใช่​บรรทัด​ที่​ดู​เหมือน​สวิตช์
  • สอง​กลไก​นี้​แยก​กัน และ​ตัว​ที่​ปิด​รู​ของ GET ที่​เขียน คือ CORS

สิ่ง​ที่​คอร์ส​นี้​ไม่​ได้​ตอบ และ​ไม่​ควร​ถูก​อ่าน​ว่า​ตอบ​แล้ว

  • พฤติกรรม​ข้าม​ไซต์​ของ SameSite ยัง​ไม่​ได้​วัด ด้วย​เหตุผล​ใน​กล่อง​ข้าง​บน
  • สิ่ง​ที่​หน้า​เว็บ​เห็น​เมื่อ​เจอ 500 ของ​ทรง wildcard คู่​กับ credentials ยัง​ไม่​ได้​วัด
  • อายุ​ที่ preflight ถูก​เก็บ​ไว้​ใช้​ซ้ำ ข้อ​นั้น​มี​นาฬิกา​อยู่​ข้าง​ใน จึง​ตก​นอก​หน่วย​ของ​คอร์ส​นี้
  • OAuth2 · OIDC และ​หน้า login คอร์ส​ที่ 35 ตัด​ออก​ด้วย​เหตุผล​เดียวกัน

หน่วย​ของ​หลัก​ฐาน​ที่ probe ใช้​ทั้ง​ชุด​มี​สี่​หน่วย คือ​จำนวน​แถว​ใน csrf_visit รหัส HTTP จำนวน header และ​ตัว​อักษร​ของ Set-Cookie ส่วน​ชุด​วัด​ฝั่ง​เบราว์เซอร์​นับ​จำนวน​คำขอ ที่​ปลาย​ทาง​ได้​รับ และ​ไม่มี​ข้อ​อ้าง​ข้อ​ไหน​ใน​คอร์ส​นี้​อ่าน​นาฬิกา


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Spring Security — Cross Site Request Forgery (CSRF) (เข้า​ถึง 2026-09-05) — ที่มา​ของ​ความ​หมาย​ของ​กลไก​กัน​ปลอม และ​ของ safe method ที่​มัน​ไม่​ตรวจ

  • Spring Framework — CORS configuration (เข้า​ถึง 2026-09-05) — ที่มา​ของ CorsConfiguration และ​ของ​กฎ​ที่​ห้าม allowedOrigins("*") คู่​กับ allowCredentials

  • Fetch Standard — CORS protocol (เข้า​ถึง 2026-09-05) — ที่มา​ของ CORS protocol · หัวข้อ​นี้​เขียน​ไว้​เอง​ว่า​มัน​อธิบาย protocol ใน​มุม​ของ​ผู้​พัฒนา​ฝั่ง server ส่วน​ข้อ​กำหนด​ของ user agent อยู่​ใน​ขั้น​ตอน fetch · บท​นี้​อ้าง​มัน​เป็น​สเปก​เท่านั้น ไม่​ได้​วัด​มัน

  • ตัวเลข​ทุก​ตัว​ใน​บท​นี้​มา​จาก scripts/clinic-cross-origin/probe.py ซึ่ง​สร้าง​โปร​เจ​กต์ ขึ้น​ใหม่​ที​ละ​ทรง​แล้ว​เทียบ​ผล​ที​ละ​ช่อง · ต้อง​สั่ง scripts/clinic-cross-origin/up.sh ก่อน ซึ่ง​ยก container ของ​คอร์ส​นี้​เอง​ขึ้น​ที่ port 55437

เช็กความเข้าใจ — บทที่ 8

ข้อ 1 / 3

ยิง `POST /visits` ที่ไม่มี CSRF token ใส่ทรง `csrf-on` และทรง `csrf-on-cors` ผลต่างกันตรงไหน