Section 6

นักศึกษากำลังตรวจความปลอดภัย การแสดงผลบนมือถือ และทบทวนระบบ Booklab ที่เสร็จสมบูรณ์
SECTION 6 · พร้อมส่งงาน

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


Part 1 — ความปลอดภัยที่ต้องรู้

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

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

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

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

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

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

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

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

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

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

Angular ป้องกันให้อัตโนมัติ เวลาแสดงค่าด้วย {{ }} 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 เป็นช่องโหว่ (vulnerability) ที่ทำให้ระบบใหญ่ ๆ ล่มมาแล้วนับไม่ถ้วน

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

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

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

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

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

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

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

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

นี่คือคำตอบของคำถามตั้งแต่ Section แรก ว่าทำไม frontend ต่อฐานข้อมูลตรง ๆ ไม่ได้ ถ้าเอา 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 เท่านั้น

1.5 ทดสอบ API ด้วย Postman (ทางเลือกแทน PowerShell)

curl.exe ใช้ได้ผลดี แต่พิมพ์ JSON ยาว ๆ ใน PowerShell ต้องคอย escape เครื่องหมายคำพูดจนอ่านยาก Postman คือโปรแกรมที่ทำหน้าที่เดียวกัน (ยิง request ไปหา API) แต่มีหน้าจอกรอกข้อมูลแทนการพิมพ์คำสั่ง เหมาะกับตอนต้องทดสอบ API บ่อย ๆ หรือ endpoint ที่ซับซ้อน

1.5.1 เปิดใช้งาน Postman บนเบราว์เซอร์

  1. เปิดเบราว์เซอร์ไปที่ postman.com แล้วกด Sign up for free (สมัครด้วยอีเมลหรือบัญชี Google ก็ได้ ไม่มีค่าใช้จ่าย)
  2. หลังสมัครเสร็จจะเข้าสู่ Postman Web ที่ web.postman.co โดยอัตโนมัติ — ใช้งานผ่านเบราว์เซอร์ล้วน ๆ ไม่ต้องติดตั้งโปรแกรมตัวเต็ม

⚠️ ต้องติดตั้ง "Postman Desktop Agent" ก่อนยิง API ที่ localhost ได้ เบราว์เซอร์มีกฎความปลอดภัยที่กันเว็บหนึ่ง (web.postman.co) ไม่ให้ยิง request ตรงไปที่ localhost ของเครื่องเรา Postman จึงต้องอาศัยโปรแกรมเล็ก ๆ ชื่อ Desktop Agent รันอยู่เบื้องหลังเป็นตัวกลางแทน — เมื่อเข้าเว็บ Postman ครั้งแรกจะมีข้อความแจ้งให้ดาวน์โหลด กดลิงก์ที่ให้มา ติดตั้ง แล้วเปิดโปรแกรมทิ้งไว้ (ไม่ต้อง login ซ้ำ) จากนั้นกลับไปที่แท็บเบราว์เซอร์ มุมขวาบนจะขึ้นจุดสีเขียว "Connected via Desktop Agent" แปลว่าพร้อมใช้งาน

อย่าสับสน Desktop Agent ไม่ใช่ Postman เวอร์ชันเต็ม เป็นแค่ตัวกลางเล็ก ๆ ที่ทำงานเงียบ ๆ อยู่เบื้องหลัง หน้าจอที่ใช้งานจริงยังคงเป็น web.postman.co บนเบราว์เซอร์เหมือนเดิม

1.5.2 รู้จักหน้าจอพื้นฐาน

ส่วนของหน้าจอ ใช้ทำอะไร
แถบซ้ายสุด (Sidebar) รายการ Workspace และ Collection — ที่เก็บ request ที่เคยสร้างไว้ เรียกกลับมาใช้ซ้ำได้
Dropdown ตัวอักษร GET ด้านซ้ายของช่อง URL เลือกประเภทคำขอ (GET, POST, PUT, DELETE ฯลฯ) — ค่าเริ่มต้นเป็น GET เสมอ ต้องเปลี่ยนเองถ้าจะส่งข้อมูล
ช่อง URL ยาว ๆ ตรงกลาง พิมพ์ปลายทางที่จะยิง เช่น http://localhost:3000/api/books
ปุ่ม Send สีส้มด้านขวา กดเพื่อส่งคำขอจริง
แถบแท็บ Params / Authorization / Headers / Body ใต้ช่อง URL ตั้งค่ารายละเอียดของคำขอ — ที่ใช้บ่อยที่สุดคือ Body สำหรับใส่ข้อมูล JSON ตอนส่ง POST/PUT
พื้นที่ด้านล่างหลังกด Send Response — แสดงรหัสสถานะ (เช่น 200, 400), เวลาที่ใช้, ขนาดข้อมูล และเนื้อหาที่ API ตอบกลับ

1.5.3 ลองยิง request เดียวกับที่เพิ่งทำด้วย curl

  1. กด + (แท็บใหม่) หรือปุ่ม New → HTTP Request
  2. เปลี่ยน dropdown จาก GET เป็น POST
  3. ช่อง URL พิมพ์ http://localhost:3000/api/books
  4. คลิกแท็บ Body → เลือก raw → มุมขวาของแท็บเปลี่ยนจาก Text เป็น JSON (ขั้นนี้สำคัญ ถ้าลืมเปลี่ยน API จะไม่รู้ว่าข้อมูลที่ส่งมาเป็น JSON)
  5. พิมพ์ในกล่อง Body
json
{
  "title": "",
  "author": "x",
  "year": 2020,
  "categoryId": 1
}
  1. กด Send

✅ ต้องเห็น ในช่อง Response: สถานะ 400 Bad Request และเนื้อหาข้อความ "กรุณากรอกชื่อหนังสือ" — ผลลัพธ์เดียวกับตอนใช้ curl.exe เป๊ะ เพราะยิงไปหา endpoint และ ValidationPipe ตัวเดียวกัน

💡 ข้อดีของ Postman เทียบกับพิมพ์คำสั่งเอง

  • ไม่ต้อง escape เครื่องหมายคำพูดเหมือนใน PowerShell — พิมพ์ JSON ได้ตามปกติ
  • กด Save เก็บ request ไว้เป็น Collection เรียกกลับมายิงซ้ำได้ ไม่ต้องพิมพ์ใหม่ทุกครั้ง
  • อ่านผลลัพธ์ง่ายกว่า มีสี ปุ่มพับ/ขยาย JSON ให้อัตโนมัติ (Pretty view)

❌ ถ้าขึ้น "Could not send request" หรือ error เกี่ยวกับ Agent → Desktop Agent ยังไม่ได้เปิด หรือปิดไปแล้ว กลับไปเปิดโปรแกรม Desktop Agent อีกครั้ง (ดู 1.5.1) แล้วกด Send ซ้ำ


Part 2 — เก็บงานให้เรียบร้อย

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

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

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

2.1 เพิ่มหน้า 404

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

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

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

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 ไม่ผ่านก็ได้ จึงต้องทดสอบก่อนเสมอ


Part 3 — ทบทวนภาพรวมและถาม-ตอบ

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

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • ✅ Angular (frontend) (ทำไปแล้ว)
  • ✅ NestJS (backend) (ทำไปแล้ว)
  • ✅ PostgreSQL (ฐานข้อมูล) (ทำไปแล้ว)

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

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

แผนภาพเส้นทางข้อมูล Booklab: ผู้ใช้ใช้ Angular บน port 4200 ผ่าน HttpClient และ CORS ไปยัง NestJS API บน port 3000 จากนั้น BooksController และ BooksService ใช้ Prisma Client ติดต่อ PostgreSQL ก่อนส่ง JSON response กลับไปอัปเดต Book List

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

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

คำถามชวนคิด

  • ทำไม frontend ถึงต่อฐานข้อมูลตรง ๆ ไม่ได้
  • 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 เป็นและอ่านผลได้

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


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

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

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