Private Docs

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)

  1. ดึง API contract ทั้งหมด: swagger.json ของแต่ละ service (มี consent-swagger.json เป็นตัวอย่างอยู่แล้ว)
  2. ไล่ทุก 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
  1. บันทึกเป็นตาราง: endpoint · method · field · class · ทิศทาง (FE→BE / BE→FE / BE→BE) · เก็บที่ไหน (DB/Redis/log) · จำเป็นไหม
  2. Output → เก็บเป็น PII_INVENTORY_{SERVICE}.md ใน docs + ใช้เป็น input ของ 8.2, [Encrypt] rollout, และคำตอบผู้ตรวจ

งาน 8.2 — Data Minimization (แก้ที่ต้นเหตุ “PII ส่งกันหลายจุด”)

ต้นเหตุที่พบบ่อย: endpoint คืน entity เต็มก้อน (ทุก field) ทั้งที่หน้าจอใช้ 3 field

#จุดAction
1Application layer ทุก serviceResponse ต้องเป็น DTO เฉพาะ use case ไม่ map entity ตรงๆ — audit query handler ที่ ProjectTo/map ทั้ง entity
2จาก inventory 8.1field class P0/P1 ที่หน้าจอไม่ใช้ → ตัดออกจาก response DTO
3List endpointsต้องมี pagination (API-GL-001 §7(7.3)) + field ย่อกว่า detail endpoint

งาน 8.3 — Masking Convention กลาง

#ไฟล์Action
1Backend_Package/src/Common/Masking/MaskingExtensions.cs (ใหม่)มาตรฐานเดียวทั้ง platform: citizenId.MaskCitizenId()1-23XX-XXXXX-XX-1, email.MaskEmail()su***@e***.com, phone.MaskPhone()08X-XXX-1234
2Response 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
1ShowPII — ทุก service (if(!IsProduction) ShowPII=true มาจาก Template)เปลี่ยนเป็นเปิดเฉพาะ local — แก้ผ่าน helper กลาง (IMPL_PLAN_05 ตัวที่ 5) — SIT/UAT ตอนนี้เสี่ยง PII/token ใน auth error log
2Serilog enricher/destructuring กลางใน Packagemasking policy สำหรับ property ชื่อเข้าข่าย (citizenId, password, token, otp) — destructure แล้ว mask อัตโนมัติ
3Audit log sink ที่มีอยู่สุ่มตรวจ log จริงบน dev/SIT หา PII (email, OTP เคยมีประวัติ) → แก้ต้นทางที่ log statement
4SignatureValidationMiddleware เก็บ 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

  1. Retention policy ต่อ data type จาก inventory (เช่น KYC docs เก็บ X ปีตาม กม. → ลบอัตโนมัติ) — implement เป็น scheduled job ต่อ service ที่ถือข้อมูล
  2. กระบวนการเรียกเก็บ/ตรวจรับหลักฐานการทำลายข้อมูล จาก third party (guideline V3.0 ข้อ 3.1 — ทิศทาง: partner ส่งหลักฐานให้เรา) — runbook + template หนังสือ + registry บันทึก
  3. 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

⚠️ ข้อควรระวัง

  1. Minimization = แก้ API contract → ต้อง coordinate FE/BE — ทำเป็น versioned endpoint (/v2/) ถ้า breaking
  2. Masking ที่ server แปลว่าเคส admin ที่ต้องเห็นเต็มต้องมี endpoint + policy แยก + [Audit] — อย่าแก้ด้วย “ส่งเต็มแล้วให้ FE mask”
  3. อย่ารอ inventory เสร็จ 100% ทุก service ค่อยเริ่ม 8.2-8.5 — เสร็จ UserService แล้วเริ่ม apply เลย (iterate)

🎯 ถ้าเป็นผมจะทำอย่างไร (ความเห็น Fable)

  1. 8.1 inventory เป็น dependency ของ 4 แผนอื่น (encrypt field, RSA FE→BE, Redis PII, report DTO) — เริ่มวันนี้ได้โดยไม่ต้องรอที่ประชุม — ใช้ swagger.json + AI-assisted classification ร่างแรกแล้วให้คน review จะเร็วกว่าไล่มือหลายเท่า
  2. 8.4 ShowPII คือ quick win ที่สุดของหมวดนี้ — SIT/UAT กำลังเสี่ยง PII/token ใน log อยู่ตอนนี้ · แก้ผ่าน helper กลางใน IMPL_PLAN_05 เรียงก่อน masking enricher
  3. 8.5 frontend audit: มีหลักฐานจริงแล้ว — scan เบื้องต้นเจอ F1 (token ใน localStorage), F4 (console.log profile), F5 (bypassSecurityTrust default) — ดู IMPL_PLAN_10 §9 · checklist หมวดนี้ไม่ใช่ hypothetical อีกต่อไป
  4. Masking convention (8.3) ทำใน Package ครั้งเดียว + property-name-based auto-mask ใน Serilog — อย่าหวังพึ่ง discipline ของ dev ที่ต้องจำ mask เองทุกจุด — automated ชนะ manual เสมอในระยะยาว
  5. PDPA process (8.6) ผมจะแยก owner ชัด: compliance เขียน policy · dev ทำ endpoint/job — อย่าให้ dev เดา retention period เอง (เป็นการตัดสินทางกฎหมาย ไม่ใช่ทางเทคนิค)

📚 อ้างอิงมาตรฐาน (สำหรับใช้ในที่ประชุม):

หลักการแหล่งอ้างอิง
กฎหมาย PDPA + ประกาศคณะกรรมการคุ้มครองข้อมูลส่วนบุคคล (หน่วยงานกำกับโดยตรง)สำนักงานคณะกรรมการคุ้มครองข้อมูลส่วนบุคคล (สคส.)
Excessive data exposure / ส่ง entity เต็มแล้วซ่อนด้วย UIOWASP 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