Security, LLM-Safe Coding, and Final Demo
Day 5 - ชั่วโมงที่ 4: ตรวจความปลอดภัยและเตรียม Final Demo
เป้าหมายของชั่วโมงนี้
หลังเรียนจบ คุณจะสามารถ:
- อธิบายได้ว่า request เดินทางจากหน้าเว็บไปถึง database อย่างไร
- เชื่อมความเสี่ยงจาก OWASP กับ Issue Tracker ได้
- ตรวจโค้ดจาก AI ก่อนนำมาใช้จริงได้
- ตรวจระบบบน Production ด้วย USER และ ADMIN ได้
- สาธิต project พร้อมอธิบายเหตุผลของ code ที่เขียนได้
สิ่งที่ใช้
โค้ด Issue Tracker ที่ทำเสร็จแล้ว
Supabase Dashboard
Vercel Dashboard และ Production URLชั่วโมงนี้จะไม่เพิ่ม feature ใหม่ แต่จะตรวจว่าระบบที่สร้างมาทำงานครบและปลอดภัยตามขอบเขตของ project
Slide 1: ทบทวนสิ่งที่ทำใน Hour 3
ชั่วโมงที่แล้วเราเพิ่ม Authorization เพื่อให้ผู้ใช้แต่ละคนทำสิ่งต่างกันตาม role:
| ส่วนที่เพิ่ม | หน้าที่ |
|---|---|
profiles | เก็บ role USER และ ADMIN |
created_by | บอกว่า issue เป็นของผู้ใช้คนใด |
| RLS Policies | ให้ USER เห็นและสร้าง issue ของตัวเอง ส่วน ADMIN เห็นทั้งหมดและเปลี่ยน status ได้ |
requireUser() | ป้องกันหน้าที่ต้อง Login |
requireAdmin() | ป้องกันหน้าและ Server Action สำหรับ ADMIN |
canManage | แสดงเครื่องมือจัดการเฉพาะหน้าที่อนุญาต |
ตอนนี้ระบบจึงไม่ได้รู้แค่ว่า ใคร Login อยู่ แต่รู้ด้วยว่า คนนั้นทำอะไรกับข้อมูลใดได้บ้าง
ใน Hour 4 เราจะนำระบบที่ทำงานแล้วมาตรวจอีกครั้งว่าแต่ละชั้นปลอดภัย ใช้งานบน Production ได้ และพร้อมสำหรับ Final Demo
Slide 2: ระบบทำงานได้ ยังไม่แปลว่าปลอดภัย
ก่อนให้ request ทำงานต่อ ระบบควรตอบคำถามเหล่านี้:
ใครกำลังใช้งาน? → Authentication
เขาทำสิ่งนี้ได้หรือไม่? → Authorization
ข้อมูลมีรูปแบบถูกต้องไหม? → Validation
เขาเข้าถึง row นี้ได้ไหม? → RLS
ถ้าล้มเหลว เรารู้สาเหตุไหม? → Error handling และ Logsตัวอย่าง: USER ไม่เห็นปุ่มเปลี่ยน status ไม่ได้แปลว่าปลอดภัย เพราะผู้ใช้อาจส่ง request เองได้
ระบบจึงต้องตรวจ requireAdmin() ใน Server Action และให้ RLS ตรวจสิทธิ์ซ้ำที่ database
Slide 3: รู้จัก OWASP ผ่าน Issue Tracker
OWASP Top 10 คือรายการความเสี่ยงสำคัญของเว็บ ใช้เป็นแนวทางช่วยตั้งคำถามกับระบบ ไม่ใช่สิ่งที่ต้องท่องจำทุกข้อ
| ความเสี่ยง | ถ้าเกิดใน Issue Tracker จะเป็นอย่างไร |
|---|---|
| Broken Access Control | USER เปิดหน้า Admin หรือแก้ issue ของคนอื่นได้ |
| Security Misconfiguration | ยังมี demo policy หรือเปิดสิทธิ์ anon ทิ้งไว้ |
| Software Supply Chain Failures | ติดตั้ง package ที่ไม่รู้จักหรือล้าสมัย |
| Injection | นำข้อความจากผู้ใช้ไปต่อเป็น SQL string โดยตรง |
| Authentication Failures | เชื่อว่า Login แล้วทุกคนเป็น ADMIN |
| Mishandling of Exceptional Conditions | กลืน error หรือแสดงรายละเอียดระบบบนหน้าเว็บ |
เวลา review code ให้ถามว่า “ผู้ใช้ควบคุมค่าอะไรได้ และระบบตรวจค่านั้นที่ชั้นไหน”
Slide 4: ทำไมต้องป้องกัน Broken Access Control หลายชั้น
Broken Access Control คือการที่ผู้ใช้ทำสิ่งที่ตัวเองไม่มีสิทธิ์ แม้จะ Login สำเร็จแล้วก็ตาม
UI → ซ่อนลิงก์ Admin และปุ่มเปลี่ยน status จาก USER
Page Guard → ป้องกันการพิมพ์ /admin/issues โดยตรง
Server Action → ป้องกัน request ที่สร้างขึ้นเอง
RLS → ป้องกัน operation และ row ที่ databaseลองตรวจด้วย USER
- เปิด
/admin/issuesโดยตรง → ต้องกลับไป/issues - เปิด issue ของคนอื่น → ต้องไม่เห็นข้อมูล
- ส่ง update request เอง → Server Action หรือ RLS ต้องปฏิเสธ
การทดสอบว่า ADMIN ใช้งานได้อย่างเดียวไม่พอ ต้องพิสูจน์ด้วยว่า USER ทำสิ่งที่ไม่ได้รับอนุญาตไม่ได้
Slide 5: Validation กับการป้องกัน Injection ต่างกันอย่างไร
Validation ตรวจว่าข้อมูลเหมาะกับระบบหรือไม่
title ต้องยาวอย่างน้อย 5 ตัวอักษร
description ต้องยาวอย่างน้อย 10 ตัวอักษร
status ต้องเป็น OPEN, IN_PROGRESS หรือ DONEการป้องกัน Injection แยก code ออกจากข้อมูล
- ใช้ Supabase client ส่งค่าผ่าน method เช่น
.eq()และ.insert() - อย่านำ input มาต่อเป็น SQL string เอง
- React แสดงข้อความเป็น text ตามปกติ อย่าใช้
dangerouslySetInnerHTMLกับข้อมูลจากผู้ใช้โดยไม่จำเป็น
Browser validation ช่วยให้กรอก form ง่ายขึ้น แต่ Server Action ยังต้อง validate ซ้ำ เพราะผู้ใช้ส่ง request โดยไม่ผ่าน form ได้
เมื่อเกิด error ให้เก็บรายละเอียดที่จำเป็นไว้ใน server log และแสดงข้อความที่เข้าใจง่ายบนหน้าเว็บ โดยไม่เปิดเผย password, token หรือ secret
Slide 6: ค่าไหนเปิดเผยได้ และค่าไหนต้องเป็น Secret
NEXT_PUBLIC_SUPABASE_URL → ใช้ใน browser ได้
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY → ใช้ใน browser ได้
service_role key → ห้ามส่งไป browserPublishable key บอกว่าแอปกำลังเชื่อม Supabase project ใด แต่ไม่ได้ให้สิทธิ์ข้าม RLS ความปลอดภัยของข้อมูลจึงขึ้นอยู่กับ GRANT และ policies ที่เราเขียน
ก่อน Push Code
.env.localต้องอยู่ใน.gitignore.env.exampleมีเฉพาะชื่อ variable และไม่มีค่าจริง- ตรวจ
git statusก่อน commit - ห้ามใส่ password, token หรือ service role key ใน README
ติดตั้งเฉพาะ package ที่รู้ว่าใช้ทำอะไร และตรวจชื่อ package จาก documentation ทางการก่อนรันคำสั่งที่ AI แนะนำ
Slide 7: ใช้ AI ช่วยเขียน Code โดยไม่เชื่อทันที
มอง code จาก AI เหมือน code ที่คัดลอกจากอินเทอร์เน็ต: ใช้เป็นจุดเริ่มต้นได้ แต่ต้องตรวจเองก่อน
1. ให้ AI อธิบายว่า code เปลี่ยนอะไรและทำงานฝั่งไหน
2. เทียบ API กับ documentation ของ version ที่ project ใช้
3. อ่าน diff และตรวจทุกไฟล์ที่เปลี่ยน
4. รัน build และทดสอบ flow จริง
5. ทดสอบข้อมูลผิด, ไม่มี session และไม่มีสิทธิ์อย่าส่ง .env.local, password, token, API secret หรือข้อมูลส่วนบุคคลให้ AI
ก่อนใช้ code ที่เกี่ยวกับสิทธิ์ ต้องตอบให้ได้ว่า:
- user และ role ถูกตรวจที่ไหน
- field ใดมาจาก browser และ field ใดมาจาก session
- database มี RLS policy รองรับ operation นี้หรือไม่
ถ้ายังอธิบายไม่ได้ แปลว่ายังไม่ควรนำ code นั้นไปใช้จริง
Slide 8: ตรวจ Supabase ก่อนทดสอบ Production
Table issues
- เปิด RLS
- มี SELECT, INSERT และ UPDATE policies สำหรับ
authenticated - ไม่มี demo policy ที่ใช้
using (true)หรือwith check (true) - ไม่มีสิทธิ์
anonสำหรับอ่านหรือแก้ข้อมูล
Table profiles
- เปิด RLS
- USER อ่านได้เฉพาะ profile ของตัวเอง
user@example.comมี roleUSERadmin@example.comมี roleADMIN
สร้าง issue ใหม่ด้วย USER แล้วตรวจว่า created_by ตรงกับ id ของ account นั้น
Table Editor ใช้สิทธิ์ผู้ดูแลและอาจเห็นข้อมูลที่หน้าเว็บมองไม่เห็น ดังนั้นการเห็น row ใน Table Editor ยังไม่ยืนยันว่า SELECT policy ของ USER ทำงานถูกต้อง
Slide 9: Deploy Code ล่าสุดและทดสอบของจริง
ท้าย Hour 3 เรา push code ระบบ Auth และ Authorization แล้ว หาก GitHub เชื่อมกับ Vercel จะเกิด Production Deployment ใหม่
1. เปิด Vercel และตรวจว่า deployment มาจาก commit ล่าสุด
2. รอให้ build สำเร็จ
3. ตรวจ Environment Variables ของ Production
4. เปิด Production URL และทดสอบ USER/ADMIN
5. หากแก้ env vars ต้อง redeploy เพื่อให้ deployment ใหม่อ่านค่าFlow ที่ต้องผ่านบน Production
- USER Login → สร้าง issue → เห็นเฉพาะ issue ของตัวเอง
- USER เปิด
/admin/issues→ ถูกส่งกลับ/issues - ADMIN Login → เห็น issue ทั้งหมด → เปลี่ยน status ได้
- Logout → protected pages ส่งไป
/login
ก่อนส่ง project ให้เพิ่ม Production URL, วิธีรัน และชื่อ environment variables ที่ต้องใช้ไว้ใน README โดยไม่ใส่ค่าจริงหรือ password
Slide 10: เมื่อระบบมีปัญหา ให้หาว่าเสียที่ชั้นไหน
อย่าแก้หลายไฟล์พร้อมกัน ให้เริ่มจากอาการแล้วตรวจชั้นที่เกี่ยวข้อง:
ปุ่มหรือข้อมูลบนหน้าผิด → ตรวจ props และ conditional UI
กด form แล้วไม่ทำงาน → ตรวจ Server Action และ server logs
อ่าน/เขียน table ไม่ได้ → ตรวจ Supabase error, GRANT และ RLS
ข้อมูลเข้าแต่หน้า list ว่าง → ตรวจ created_by, session และ SELECT policy
Local ใช้ได้ Production ไม่ได้ → ตรวจ env vars, deployment และ Supabase projectหลังจากนี้คุณสามารถต่อยอดได้
- เพิ่ม automated tests สำหรับ USER และ ADMIN
- เพิ่ม error states ที่ช่วยให้ผู้ใช้แก้ปัญหาได้
- เพิ่ม pagination และ search เมื่อข้อมูลมากขึ้น
- เพิ่ม audit log และ monitoring สำหรับการใช้งานจริง
เป้าหมายของหลักสูตรไม่ใช่จำทุก syntax แต่คืออ่าน flow ออก รู้ว่าควรตรวจชั้นไหน และอธิบายเหตุผลของ code ที่นำไปใช้ได้