IMPL PLAN 08 — Sensitive Data / PDPA
เริ่มจาก PII Inventory ก่อนทุกอย่าง แล้วไล่ minimization → masking → log hygiene → frontend audit
อัปเดต: 2026-07-07
IMPL PLAN 08 — Sensitive Data / PDPA (PII ระหว่าง Frontend ↔ Backend)
วันที่: 07/07/2026 · สถานะ: ⏳ รอ verify · Priority: 🟠 Phase 2 (8.1 เริ่มได้ทันที — เป็น prerequisite ของ encryption) อ้างอิง: DISCUSSION หมวด 8 · โจทย์ทีม: “PII รับส่งกันระหว่าง Backend/Frontend หลายจุด”
สรุป
หมวดนี้ยังไม่มี systematic control — ทุกอย่างเริ่มจาก 8.1 PII Inventory เพราะไม่มี inventory = ตัดสินใจไม่ได้ว่า field ไหน encrypt/mask/ตัดทิ้ง · จากนั้นไล่: minimization → masking → log hygiene → frontend audit → PDPA process
งาน 8.1 — PII Inventory + Data Classification (ทำก่อน — ทุกงานอื่นรอตัวนี้)
วิธีทำ (ต่อ service — เริ่ม UserService เป็น pilot)
- ดึง API contract ทั้งหมด: swagger.json ของแต่ละ service (มี
consent-swagger.jsonเป็นตัวอย่างอยู่แล้ว) - ไล่ทุก endpoint × ทุก field → ติด class:
| Class | นิยาม | ตัวอย่าง | Control ขั้นต่ำ |
|---|---|---|---|
| P0 — Sensitive PII | ระบุตัว + อ่อนไหวสูง (PDPA มาตรา 26 ระวังพิเศษ) | เลขบัตร ปชช., passport, ข้อมูลชีวมิติ, KYC docs | [Encrypt] at-rest · ไม่อยู่ใน URL/log เด็ดขาด · mask ทุก response ที่ไม่จำเป็นต้องเห็นเต็ม · FE→BE พิจารณา RSA field encryption (IMPL_PLAN_02 §4.2) |
| P1 — PII ทั่วไป | ระบุตัวได้ | ชื่อ-สกุล, email, เบอร์, ที่อยู่, วันเกิด | ไม่อยู่ใน URL · log ต้อง mask · ส่งเฉพาะหน้าจอที่ใช้ |
| P2 — Internal | ข้อมูลธุรกิจไม่ระบุตัว | order id, สถานะ, config | ควบคุมด้วย auth ปกติ |
| P3 — Public | เปิดเผยได้ | ข่าว, banner, master data | — |
- บันทึกเป็นตาราง:
endpoint · method · field · class · ทิศทาง (FE→BE / BE→FE / BE→BE) · เก็บที่ไหน (DB/Redis/log) · จำเป็นไหม - Output → เก็บเป็น
PII_INVENTORY_{SERVICE}.mdใน docs + ใช้เป็น input ของ 8.2,[Encrypt]rollout, และคำตอบผู้ตรวจ
งาน 8.2 — Data Minimization (แก้ที่ต้นเหตุ “PII ส่งกันหลายจุด”)
ต้นเหตุที่พบบ่อย: endpoint คืน entity เต็มก้อน (ทุก field) ทั้งที่หน้าจอใช้ 3 field
| # | จุด | Action |
|---|---|---|
| 1 | Application layer ทุก service | Response ต้องเป็น DTO เฉพาะ use case ไม่ map entity ตรงๆ — audit query handler ที่ ProjectTo/map ทั้ง entity |
| 2 | จาก inventory 8.1 | field class P0/P1 ที่หน้าจอไม่ใช้ → ตัดออกจาก response DTO |
| 3 | List endpoints | ต้องมี pagination (API-GL-001 §7(7.3)) + field ย่อกว่า detail endpoint |
งาน 8.3 — Masking Convention กลาง
| # | ไฟล์ | Action |
|---|---|---|
| 1 | Backend_Package/src/Common/Masking/MaskingExtensions.cs (ใหม่) | มาตรฐานเดียวทั้ง platform: citizenId.MaskCitizenId() → 1-23XX-XXXXX-XX-1, email.MaskEmail() → su***@e***.com, phone.MaskPhone() → 08X-XXX-1234 |
| 2 | Response DTO ที่ inventory ชี้ “ต้องโชว์บางส่วน” | ใช้ masking แทนส่งเต็ม — mask ที่ server เสมอ (mask ที่ frontend = ข้อมูลเต็มวิ่งบน network แล้ว = ไม่ช่วยอะไร) |
sequenceDiagram
autonumber
participant FE as Angular
participant BE as Backend
participant DB as PostgreSQL
rect rgb(255,235,235)
Note over FE,DB: ❌ ปัจจุบัน (pattern ที่ต้องเลิก)
FE->>BE: GET /users/123
BE->>DB: SELECT * (ทุก field)
BE-->>FE: entity เต็ม { citizenId: "1234567890123", salary, address, ... }
Note over FE: หน้าจอใช้แค่ชื่อ + เบอร์ — ที่เหลือ "แถม" ไปอยู่ใน devtools/memory
end
rect rgb(235,255,235)
Note over FE,DB: ✅ เป้าหมาย
FE->>BE: GET /users/123/summary
BE->>DB: SELECT เฉพาะ field ของ use case
BE->>BE: mask ที่ server: citizenId → "1-23XX-XXXXX-XX-1"
BE-->>FE: DTO เฉพาะ { displayName, phoneMasked, citizenIdMasked }
Note over FE: ต้องการเห็นเต็ม (เคส admin) → endpoint แยก + policy แยก + [Audit] log
end
งาน 8.4 — Log Hygiene
| # | จุด | Action |
|---|---|---|
| 1 | ShowPII — ทุก service (if(!IsProduction) ShowPII=true มาจาก Template) | เปลี่ยนเป็นเปิดเฉพาะ local — แก้ผ่าน helper กลาง (IMPL_PLAN_05 ตัวที่ 5) — SIT/UAT ตอนนี้เสี่ยง PII/token ใน auth error log |
| 2 | Serilog enricher/destructuring กลางใน Package | masking policy สำหรับ property ชื่อเข้าข่าย (citizenId, password, token, otp) — destructure แล้ว mask อัตโนมัติ |
| 3 | Audit log sink ที่มีอยู่ | สุ่มตรวจ log จริงบน dev/SIT หา PII (email, OTP เคยมีประวัติ) → แก้ต้นทางที่ log statement |
| 4 | SignatureValidationMiddleware เก็บ RequestBody/ResponseBody ใน HttpContext.Items สำหรับ log | ตรวจว่า Serilog ที่อ่านไปเขียน log ผ่าน masking ก่อน — body ของ endpoint P0 ไม่ควร log ทั้งก้อน |
งาน 8.5 — Frontend Audit (Frontend_HostAppSuperApp)
Checklist ต่อ app (shell + remotes):
-
grep -rn "localStorage\|sessionStorage" apps/ libs/— ห้ามมี PII/token (token ไม่ควรมีอยู่แล้วเพราะ cookie façade — verify) -
grep -rn "console.log\|console.error"— ไม่มี object ผู้ใช้/response ทั้งก้อน · ปิด console ใน production build - Form field P0:
autocomplete="off"+type="password"-style masking ตามเหมาะสม - ไม่ส่ง PII เป็น query param ใน router navigation (
/user?citizenId=❌ — ใช้ state/POST) - Error interceptor (
auth-error.interceptor.tsมีอยู่) — ไม่ toast/แสดง raw error body ที่อาจมี PII - Cache ของ HTTP layer — ไม่ cache response P0 (Cache-Control ฝั่ง server ต้องตั้ง
no-storeด้วย)
งาน 8.6 — PDPA Process
- Retention policy ต่อ data type จาก inventory (เช่น KYC docs เก็บ X ปีตาม กม. → ลบอัตโนมัติ) — implement เป็น scheduled job ต่อ service ที่ถือข้อมูล
- กระบวนการเรียกเก็บ/ตรวจรับหลักฐานการทำลายข้อมูล จาก third party (guideline V3.0 ข้อ 3.1 — ทิศทาง: partner ส่งหลักฐานให้เรา) — runbook + template หนังสือ + registry บันทึก
- Data subject rights — มี flow รองรับ “ขอลบ/ขอดูข้อมูล” ไหม → gap ให้ทีม compliance กำหนด SLA แล้วทีม dev ทำ endpoint/job รองรับ
Verify
-
PII_INVENTORY_USERSERVICE.mdเสร็จเป็น template ให้ service อื่น - Response ของ 5 endpoint ที่ PII หนักสุด → เหลือเฉพาะ field ที่หน้าจอใช้ + mask
- Log บน SIT ไม่มี PII (สุ่มตรวจหลังแก้ ShowPII + enricher)
- Frontend checklist ผ่านทุกข้อ + มี CI grep gate (localStorage/console.log)
- PDPA runbook 3 ตัวมี owner ฝั่ง compliance
⚠️ ข้อควรระวัง
- Minimization = แก้ API contract → ต้อง coordinate FE/BE — ทำเป็น versioned endpoint (
/v2/) ถ้า breaking - Masking ที่ server แปลว่าเคส admin ที่ต้องเห็นเต็มต้องมี endpoint + policy แยก +
[Audit]— อย่าแก้ด้วย “ส่งเต็มแล้วให้ FE mask” - อย่ารอ inventory เสร็จ 100% ทุก service ค่อยเริ่ม 8.2-8.5 — เสร็จ UserService แล้วเริ่ม apply เลย (iterate)
🎯 ถ้าเป็นผมจะทำอย่างไร (ความเห็น Fable)
- 8.1 inventory เป็น dependency ของ 4 แผนอื่น (encrypt field, RSA FE→BE, Redis PII, report DTO) — เริ่มวันนี้ได้โดยไม่ต้องรอที่ประชุม — ใช้ swagger.json + AI-assisted classification ร่างแรกแล้วให้คน review จะเร็วกว่าไล่มือหลายเท่า
- 8.4 ShowPII คือ quick win ที่สุดของหมวดนี้ — SIT/UAT กำลังเสี่ยง PII/token ใน log อยู่ตอนนี้ · แก้ผ่าน helper กลางใน IMPL_PLAN_05 เรียงก่อน masking enricher
- 8.5 frontend audit: มีหลักฐานจริงแล้ว — scan เบื้องต้นเจอ F1 (token ใน localStorage), F4 (console.log profile), F5 (bypassSecurityTrust default) — ดู IMPL_PLAN_10 §9 · checklist หมวดนี้ไม่ใช่ hypothetical อีกต่อไป
- Masking convention (8.3) ทำใน Package ครั้งเดียว + property-name-based auto-mask ใน Serilog — อย่าหวังพึ่ง discipline ของ dev ที่ต้องจำ mask เองทุกจุด — automated ชนะ manual เสมอในระยะยาว
- PDPA process (8.6) ผมจะแยก owner ชัด: compliance เขียน policy · dev ทำ endpoint/job — อย่าให้ dev เดา retention period เอง (เป็นการตัดสินทางกฎหมาย ไม่ใช่ทางเทคนิค)
📚 อ้างอิงมาตรฐาน (สำหรับใช้ในที่ประชุม):
| หลักการ | แหล่งอ้างอิง |
|---|---|
| กฎหมาย PDPA + ประกาศคณะกรรมการคุ้มครองข้อมูลส่วนบุคคล (หน่วยงานกำกับโดยตรง) | สำนักงานคณะกรรมการคุ้มครองข้อมูลส่วนบุคคล (สคส.) |
| Excessive data exposure / ส่ง entity เต็มแล้วซ่อนด้วย UI | OWASP API3 — Broken Object Property Level Authorization |
| Privacy by design ฝั่ง engineering (minimization, retention) | OWASP User Privacy Protection Cheat Sheet |
| ห้าม log อะไรบ้าง (PII, credentials, session id) | OWASP Logging Cheat Sheet — Data to exclude |
| Privacy risk framework (ใช้จัด class P0-P3 อย่างเป็นระบบ) | NIST Privacy Framework |