📘 Booklab Workshop

Day 6

ความปลอดภัย + ทบทวนภาพรวม

เวลา: 120 นาที จบคาบนี้จะได้: เข้าใจช่องโหว่พื้นฐานที่ระบบนี้ป้องกันให้อัตโนมัติ และทบทวนภาพรวมทั้งหลักสูตรผ่านแบบฝึกหัดเพิ่มเติม


ช่วงที่ 1 — ความปลอดภัยที่ต้องรู้ (25 นาที)

🗺️ ตำแหน่งตอนนี้ในระบบ

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • 🟢 Angular (หน้าบ้าน) ← กำลังทำช่วงนี้
  • 🟢 NestJS (หลังบ้าน) ← กำลังทำช่วงนี้
  • 🟢 PostgreSQL (ฐานข้อมูล) ← กำลังทำช่วงนี้

ต่อไป: เก็บรายละเอียดปลีกย่อยให้เว็บพร้อมใช้งานจริง

1.1 ทดลองโจมตีเว็บของตัวเอง

เปิด localhost:4200/books/new แล้วกรอกชื่อหนังสือว่า

text
<script>alert('ถูกแฮกแล้ว')</script>

กดบันทึก แล้วดูหน้ารายการ

✅ ต้องเห็นเป็นข้อความธรรมดา ไม่มีกล่องเด้ง

ผลลัพธ์การทดสอบ XSS — เห็นแท็ก script เป็นข้อความธรรมดา ไม่มีกล่องเด้ง

💡 การโจมตีแบบนี้เรียกว่า XSS — คือการแอบฝังโค้ดลงในช่องกรอกข้อมูล หวังให้เว็บเอาไปรันจริง ถ้าสำเร็จ ผู้โจมตีจะขโมยข้อมูลผู้ใช้คนอื่นได้

Angular ป้องกันให้อัตโนมัติ เวลาแสดงค่าด้วย {{ }} มันจะแปลงเป็นข้อความธรรมดาเสมอ ไม่ยอมรันเป็นโค้ด

⚠️ แต่ถ้าไปใช้คำสั่งอย่าง innerHTML เองจะไม่ปลอดภัย — อย่าใช้ถ้าไม่จำเป็นจริง ๆ

ลบหนังสือเล่มนี้ทิ้งก่อนไปต่อ

1.2 ทำไม Prisma ถึงสำคัญกว่าที่คิด

สมมติเราไม่ใช้ Prisma แต่ต่อ SQL เป็นข้อความเองแบบนี้

typescript
// ❌ อันตราย ห้ามเขียนแบบนี้เด็ดขาด
const sql = "SELECT * FROM Book WHERE title = '" + keyword + "'";

ถ้าผู้ใช้พิมพ์คำค้นว่า ' OR '1'='1 ประโยคจะกลายเป็น

sql
SELECT * FROM Book WHERE title = '' OR '1'='1'

ซึ่งเป็นจริงเสมอ ทำให้ได้ข้อมูลทั้งตารางออกมา และถ้าพิมพ์ซับซ้อนกว่านี้ก็ลบทั้งตารางได้

💡 เรียกว่า SQL Injection เป็นช่องโหว่ที่ทำให้ระบบใหญ่ ๆ ล่มมาแล้วนับไม่ถ้วน

Prisma ป้องกันให้โดยอัตโนมัติ เพราะเราไม่ได้ต่อข้อความเอง แต่ส่งค่าแยกไปให้ฐานข้อมูลจัดการ ฐานข้อมูลจึงมองค่านั้นเป็น "ข้อมูล" เสมอ ไม่ใช่ "คำสั่ง"

นี่คือเหตุผลสำคัญข้อหนึ่งที่โลกจริงใช้ ORM

1.3 ตรวจว่าความลับไม่หลุด

เปิดโฟลเดอร์ apps/api ดูให้แน่ใจว่าไฟล์ .env อยู่ตรงนั้นที่เดียว

❌ ถ้าเผลอคัดลอกไฟล์นี้ไปไว้ที่อื่น (เช่น ส่งให้เพื่อนช่วยดูโค้ด หรือแนบไปกับข้อความ) ให้ลบทิ้งจากที่นั้นทันที

⚠️ ทำไมเรื่องนี้สำคัญ ถ้ามีใครเผลอส่งไฟล์นี้ให้คนอื่น เท่ากับแนบรหัสผ่านฐานข้อมูลของตัวเองไปให้คนอื่นเห็นด้วย

จากนั้นเปิด localhost:4200 กด F12 → แท็บ Sources แล้วเปิดดูไฟล์โค้ดของเรา

💡 สังเกตว่าโค้ด Angular ทั้งหมดอ่านได้หมด — ทุกคนที่เข้าเว็บเราเห็นสิ่งนี้ได้

นี่คือคำตอบของคำถามตั้งแต่คาบแรก ว่าทำไมหน้าบ้านต่อฐานข้อมูลตรง ๆ ไม่ได้ ถ้าเอา connection string ไปไว้ฝั่ง Angular เท่ากับแปะรหัสผ่านฐานข้อมูลไว้ให้คนทั้งโลกอ่าน

ส่วนโค้ดฝั่ง NestJS รันอยู่บนเซิร์ฟเวอร์ ผู้ใช้เห็นแค่ผลลัพธ์ JSON เท่านั้น ความลับจึงอยู่ฝั่งนั้นได้

1.4 ทดสอบว่า API ตรวจข้อมูลจริงไหม

ปิดหน้าเว็บไป แล้วยิงตรงไปที่ API ด้วย PowerShell

powershell
curl.exe -X POST http://localhost:3000/api/books -H "Content-Type: application/json" -d "{\"title\":\"\",\"author\":\"x\",\"year\":2020,\"categoryId\":1}"

✅ ต้องได้ error กลับมาว่า "กรุณากรอกชื่อหนังสือ"

💡 นี่คือเหตุผลที่ต้องตรวจสองชั้น การตรวจฝั่ง Angular มีไว้ให้ผู้ใช้รู้ตัวเร็ว แต่ป้องกันอะไรไม่ได้เลย เพราะคนที่ตั้งใจโกงข้ามหน้าเว็บมายิง API ตรง ๆ แบบที่เราเพิ่งทำได้

การตรวจที่ป้องกันได้จริงคือฝั่ง API เท่านั้น


ช่วงที่ 2 — เก็บงานให้เรียบร้อย (25 นาที)

🗺️ ตำแหน่งตอนนี้ในระบบ

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • 🟢 Angular (หน้าบ้าน) ← กำลังทำช่วงนี้
  • 🟢 NestJS (หลังบ้าน) ← กำลังทำช่วงนี้
  • ✅ PostgreSQL (ฐานข้อมูล) (ทำไปแล้ว)

ต่อไป: ทบทวนภาพรวมทั้งระบบก่อนจบวิชา

2.1 เพิ่มหน้า 404

แก้ app.routes.ts เพิ่มบรรทัดสุดท้าย

typescript
  { path: '**', redirectTo: '' },

💡 ** = ตรงกับทุก URL ที่ไม่ตรงกับข้างบนเลย ต้องอยู่ล่างสุดเสมอ เพราะถ้าอยู่บน มันจะกินทุกเส้นทาง

2.2 ทำให้ใช้บนมือถือได้

ตรวจว่าใน book-list.html ใช้คลาสแบบนี้

html
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">

💡 sm: และ lg: คือ responsive ของ Tailwind

  • ไม่มีคำนำหน้า = ใช้กับทุกขนาด (มือถือ)
  • sm: = จอกว้างขึ้นมาหน่อย → 2 คอลัมน์
  • lg: = จอใหญ่ → 3 คอลัมน์

หลักการนี้เรียกว่า mobile first คือออกแบบให้มือถือก่อน แล้วค่อยเพิ่มความซับซ้อนเมื่อจอใหญ่ขึ้น

ทดสอบโดยกด F12 แล้วกดไอคอนมือถือ ลองย่อขยายดู

หน้ารายการหนังสือแสดงผลบนหน้าจอมือถือ — เรียงเป็นคอลัมน์เดียว

2.3 ตรวจสอบว่าโค้ดพร้อมใช้งานจริง

powershell
cd C:\webdev\booklab
npm run build --workspace api
npm run build --workspace web

✅ ทั้งสองคำสั่งต้องไม่มี error

💡 build ต่างจาก dev อย่างไร

  • dev = โหมดพัฒนา เน้นความเร็วในการแก้ไข ไฟล์ใหญ่ มีข้อมูล debug
  • build = โหมดใช้งานจริง บีบอัดโค้ดให้เล็กที่สุด ตัดของที่ไม่ได้ใช้ทิ้ง

โค้ดที่รันได้ในโหมด dev อาจ build ไม่ผ่านก็ได้ จึงต้องทดสอบก่อนเสมอ


ช่วงที่ 3 — ทบทวนภาพรวมและถาม-ตอบ (65 นาที)

🗺️ ตำแหน่งตอนนี้ในระบบ

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • 🟢 Angular (หน้าบ้าน) ← กำลังทำช่วงนี้
  • 🟢 NestJS (หลังบ้าน) ← กำลังทำช่วงนี้
  • 🟢 PostgreSQL (ฐานข้อมูล) ← กำลังทำช่วงนี้

ต่อไป: จบหลักสูตร 6 คาบ

ไม่มีโค้ดใหม่ให้เขียนแล้ว ใช้ช่วงนี้ทบทวนเส้นทางข้อมูลทั้งระบบและเปิดให้ถามคำถามที่ค้างใจตลอด 6 คาบ

เส้นทางข้อมูลทั้งระบบ

Production Deployment Architecture & End-to-End System

text
ผู้ใช้พิมพ์
   ↓
Component (Angular)
   ↓
Service → HttpClient
   ↓
HTTP Request (CORS: 4200 → 3000)
   ↓
NestJS Controller
   ↓
NestJS Service → Prisma
   ↓
PostgreSQL
   ↓
วิ่งกลับทางเดิมมาแสดงบนหน้าจอ

ลองอธิบายเส้นทางนี้ด้วยคำพูดของตัวเอง ทีละขั้น โดยไม่ดูใบงาน — ถ้าติดขั้นไหน นั่นคือจุดที่ควรกลับไปทบทวน

คำถามชวนคิด

  • ทำไมหน้าบ้านถึงต่อฐานข้อมูลตรง ๆ ไม่ได้
  • interface กับ DTO ต่างกันตรงไหน และทำไมต้องมีทั้งสองอย่าง
  • ถ้าลบ ValidationPipe ออก จะเกิดอะไรขึ้น
  • signal กับตัวแปรธรรมดาต่างกันอย่างไร

✅ Checklist สิ่งที่ควรทำได้แล้วตอนนี้

  • npm run dev รันทั้งสองฝั่งได้บนเครื่องของตัวเอง
  • PostgreSQL รันอยู่ และ npm exec -- prisma studio เปิดได้
  • จำได้ว่า npm exec -- prisma migrate reset ทำอะไร
  • สร้าง endpoint ใหม่ใน NestJS ได้โดยไม่ต้องดูใบงาน
  • สร้าง component ใหม่ใน Angular และเพิ่ม route ได้
  • เขียน service ที่ยิง API แล้วจัดการ 3 สถานะได้
  • เปิด F12 → Network เป็นและอ่านผลได้

ถ้ายังไม่มั่นใจข้อไหน กลับไปทบทวนใบงานคาบที่เกี่ยวข้องได้เสมอ


📌 สรุปสิ่งที่ทำได้แล้วหลังจบ 12 ชั่วโมง

text
ผู้ใช้ ↔ Angular (หน้าบ้าน)
          ↕ HTTP
        NestJS (หลังบ้าน)
          ↕ Prisma
        PostgreSQL
  • ออกแบบตารางและความสัมพันธ์ในฐานข้อมูลจริง
  • สร้าง API ที่ ดู เพิ่ม แก้ ลบ ได้ครบ พร้อมตรวจสอบข้อมูล
  • สร้างหน้าเว็บที่แยกเป็นชิ้นส่วน มีหลายหน้า มีฟอร์ม
  • เชื่อมสองฝั่งเข้าด้วยกัน และจัดการกรณีที่ผิดพลาด
  • เข้าใจว่าทำไมความลับต้องอยู่ฝั่งเซิร์ฟเวอร์
  • ตรวจสอบได้ว่าโค้ด build ขึ้นใช้งานจริงผ่านไม่มี error

หัวข้อที่ควรเรียนต่อด้วยตัวเอง: ระบบล็อกอินและสิทธิ์ผู้ใช้, การอัปโหลดไฟล์, การแบ่งหน้า, การเขียนเทสต์, RxJS, Docker