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

row-level security ของ​คลินิก — เปิด​แล้ว policy ยัง​ไม่​เคย​ถูก​เรียก​ใช้

policy ผูก​อยู่​กับ​ตาราง​แล้ว เขียน​ถูก​ทุก​ตัว​อักษร และ​ไม่​เคย​ถูก​เรียก​ใช้​เลย​สัก​ครั้ง คำถาม​ของ​คอร์ส​นี้​คือ​เจ้าของ​ตาราง​ที่​สั่ง ENABLE แล้ว​อ่าน​ตาราง​หก​แถว​ของ​ตัว​เอง เห็น​กี่​แถว คำ​ตอบ​ที่​วัด​ได้​คือ 6 แถว​ครบ เติม FORCE เข้าไป​โดย​ไม่​แตะ SQL ของ policy สัก​ตัว​อักษร แล้ว​ได้ 0 แถว สิ่ง​ที่​เปลี่ยน​คือ​ธง​ใน catalog ไม่ใช่​นิพจน์​ที่​เขียน​ไว้ ธี​สิ​ส​พลิก​อีก​สอง​ชั้น หน่วย​ที่​จับ​การ​รั่ว​นี้​ได้​มี​หน่วย​เดียว​คือ​จำนวน​แถว เพราะ​คำขอ​ที่​รั่ว​สำเร็จ​ทุก​ครั้ง และ​หน่วย​นั้น​มี​จุดบอด​ของ​มัน​เอง
ความคืบหน้า0 / 8 บทเรียน

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

คำถาม​คือ policy ที่​ผูก​อยู่​กับ​ตาราง​แล้ว​และ​เขียน​ถูก​ทุก​ตัว​อักษร กัน​แถว​ของ​ผู้​เช่า​คน​อื่น ไว้​ได้​จริง​หรือ​ยัง

ชั้น​ที่​หนึ่ง เปิด​ไม่ใช่​บังคับ เจ้าของ​ตาราง​ที่​สั่ง ENABLE ROW LEVEL SECURITY แล้ว​อ่าน​ตาราง​หก​แถว​ของ​ตัว​เอง วัด​ได้ 6 แถว คือ​เท่ากับ​ตาราง​ที่​ไม่มี policy ผูก​อยู่​เลย

เติม FORCE ROW LEVEL SECURITY เข้าไป โดย​ไม่​แตะ SQL ของ policy สัก​ตัว​อักษร แล้ว​วัด​ได้ 0 แถว ตั้ง GUC เป็น​ผู้​เช่า a ได้ 3 แถว ตั้ง​เป็น b ได้ 3 แถว และ​สั่ง NO FORCE ทั้ง​ที่ GUC ยัง​ตั้ง​อยู่ กลับ​ไป​ได้ 6 แถว

สิ่ง​ที่​เปลี่ยน​ใน​สี่​ทรง​นั้น​คือ​ธง​ใน catalog ไม่ใช่​นิพจน์​ที่​เขียน​ไว้ และ​ยัง​มี​อีก​สอง​ทาง ที่​ได้ 6 แถว เหมือน​กัน คือ​ตาราง​ที่ superuser เป็น​เจ้าของ ซึ่ง probe อ่าน​ธง​ทั้ง​สอง​ใบ ได้ true ทั้ง​คู่ และ role ที่​มี​ธง BYPASSRLS ซึ่ง​อ่าน​ตาราง​ที่​สั่ง​แค่ ENABLE

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

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

ด่าน​ที่​เฝ้า​แต่​ยอด​รวม ผ่าน​การ​โจมตี​นี้​ไป​อย่าง​สะอาด

ประกาศ​ไว้​ตรง​นี้ เพราะ​หน่วย​เป็น​สิ่ง​ที่​ตัดสิน​ว่า​ข้อ​อ้าง​ไหน​เข้า​คอร์ส​นี้​ได้

ทั้ง​คอร์ส​ไม่มี​หน่วย​เวลา​สัก​ตัว หน่วย​ที่​คอร์ส​นี้​ยอมรับ​มี​เก้า​อย่าง

  • จำนวน​แถว​ที่​ผู้​เรียก​อ่าน​ได้ เป็น​หน่วย​หลัก​ของ​ทั้ง​แปดบท
  • จำนวน​แถว​ที่​คำ​สั่ง​กระทำ​จริง ซึ่ง​เป็น 0 ได้​ทั้ง​ที่​คำ​สั่ง​สำเร็จ
  • จำนวน policy ที่​ผูก​อยู่​กับ​ตาราง
  • รหัส SQLSTATE ยก​เฉพาะ​รหัส ไม่​ยก​ข้อความ เพราะ​ข้อความ​เปลี่ยน​ข้าม major
  • ธง​บู​ลีน​ใน catalog เช่น relrowsecurity และ relforcerowsecurity
  • รหัส HTTP ซึ่ง​ใช้​ใน​บท​ที่ 7 บท​เดียว
  • ความ​ยาว​และ​ค่า​ของ​สตริง​ที่ current_setting คืน​มา ซึ่ง​บท​ที่ 2 ใช้​แยก​สถานะ “ไม่​เคย​ตั้ง” ออก​จาก “ตั้ง​แล้ว​สั่ง RESET”
  • จำนวน connection ที่ pool ถือ​อยู่ และ​จำนวน backend ที่​คำขอ​วิ่ง​ผ่าน ซึ่ง​บท​ที่ 7 ใช้​ตรึง​ว่า​ทั้ง​สาม pool ถือ connection ใบ​เดียว
  • จำ​นว​น​โท​เค็​นที่​ต่าง​กัน​ระหว่าง​สาม​บรรทัด​ที่​ประกาศ pool ซึ่ง​บท​ที่ 7 ใช้​แสดง​ว่า​สาม​ทาง ต่าง​กัน​ตรง​ไหน​พอดี

ราคา​ของ policy predicate มี​นาฬิกา​อยู่​ข้าง​ใน คอร์ส​นี้​จึง​ไม่​วัด​มัน​และ​ไม่​พูด​ถึง​มัน

สอง​เวที​การ​วัด เวที​หนึ่ง​เป็น SQL ล้วน อีก​เวที​เป็น​แอป​จริง​ที่​ยิง​คำขอ HTTP

  • เวที A scripts/clinic-rls/spike.py เป็น SQL ล้วน · รัน​เมื่อ 2026-09-06 ได้ 52 ข้อ​อ้าง ตรง​ทั้งหมด · 98 ค่าที่​วัด​ได้ · 3 รอบ​ต่อ​การ​เรียก​หนึ่ง​ครั้ง · 0 ค่า​ขยับ​ระหว่าง​รอบ · ออก​ด้วย​รหัส 0
  • เวที B scripts/clinic-rls/probe.py ยก Spring Boot 4.1.1 กับ Kotlin 2.4.10 ขึ้น​จริง แล้ว​ยิง​ลำดับ​คำขอ HTTP ใส่​มัน · ตัวเลข​ที่​ได้​ถูก​ตรึง​ลง src/data/perf/clinic-rls.ts ซึ่ง​เป็น​ของ​ที่​สร้าง​ขึ้น ไม่ใช่​ของ​ที่​พิมพ์​มือ

ทั้ง​สอง​เวที​วัด​บน PostgreSQL 18.6 บน aarch64-unknown-linux-musl ใน container ชื่อ kaen-rls-pg ที่ port 55438 ซึ่ง​เป็น container ของ​คอร์ส​นี้​เอง ไม่ใช่​ของ​คอร์ส​คลินิก​ใบ​ไหน​ก่อน​หน้า

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

ประกาศ​ไว้​ตรง​นี้ เพื่อ​ไม่​ให้​ใคร​อ่าน​จบ​แล้ว​เชื่อ​ว่า​ได้​ของ​ที่​คอร์ส​นี้​ไม่​ได้​ให้

  • ไม่​วัด​ความเร็ว ราคา​ของ policy predicate มี​นาฬิกา​อยู่​ข้าง​ใน จึง​ตก​นอก​กฎ​ของ​หน่วย ที่​คอร์ส​นี้​ยอมรับ เหมือน​คอร์ส​ที่ 35 และ 39 ใน​ซี​รีส์​เดียวกัน
  • ไม่​พูด​ถึง LEAKPROOF และ​การ​รั่ว​ผ่าน​ข้อความ​ผิด​พลาด​ของ​ฟังก์ชัน​ใน WHERE เป็น​ข้อ​จำกัด​ที่​รู้จัก​กัน​ดี​ของ RLS แต่ probe ของ​คอร์ส​นี้​ไม่​ได้​วัด​มัน​สัก​ทรง
  • ไม่​แตะ RLS บน​ตาราง​แบบ partition และ​การ​สืบทอด ไม่​ได้​วัด จึง​ไม่มี​ข้อ​อ้าง
  • ไม่​แตะ pg_hba.conf TLS และ​การ​จัดการ role ทั้ง​ระบบ เป็น​คนละ​คำถาม
  • ไม่​อ้าง​ถึง RLS ของ SQL Server หรือ Oracle ทุก​ข้อ​อ้าง​ผูก​กับ PostgreSQL 18.6
  • ไม่​สอน​การ​เข้า​รหัส​ข้อมูล​ใน​แถว เป็น​เรื่อง​ของ​คอร์ส security ทั่วไป

ต้อง​เขียน SQL เป็น ส่วน​ความ​รู้​เรื่อง RLS ไม่​ต้อง​มี​มา​ก่อน​สัก​นิด

  • อ่าน​และ​เขียน SQL ของ PostgreSQL ได้ ระดับ​ที่​รู้จัก SELECT UPDATE INSERT และ​เคย​เห็น transaction มา​ก่อน
  • ไม่​ต้อง​รู้จัก row-level security มา​ก่อน ทุก​กลไก​ถูก​อธิบาย​ตอน​ที่​มัน​โผล่​ครั้ง​แรก
  • อ่าน Kotlin ออก​และ​เคย​เห็น Spring Boot มา​ก่อน ใช้​ใน​บท​ที่ 7 บท​เดียว
  • ต้อง​มี Docker เพราะ​ทุก​ข้อ​อ้าง​ใน​คอร์ส​นี้​มา​จาก probe ที่​คุณ​รัน​เอง​ได้ ด้วย scripts/clinic-rls/up.sh แล้ว​เก็บ​กวาด​ด้วย docker rm -f kaen-rls-pg
  • ตัวเลข​ทุก​ตัว​ผูก​กับ​รุ่น​ที่​ระบุ​ไว้ PostgreSQL 18.6 · Spring Boot 4.1.1 · Kotlin 2.4.10

แผนผัง​นี้​เติม​ขึ้น​เอง​เมื่อ​บท​เรียน​ถูก​เพิ่ม​เข้า​มา