Private Docs

IMPL PLAN 00 — Stop the Bleeding

3 งานเร่งด่วนก่อนทุกหมวด: ถอด hardcoded credential + rotate, ปิด JWT dev-bypass, ปิด endpoint anonymous อันตราย

อัปเดต: 2026-07-07

IMPL PLAN 00 — ปิดประตูที่เปิดอยู่ (Stop the Bleeding)

วันที่: 07/07/2026 · สถานะ: ⏳ รอ verify · Priority: 🔴 สูงสุด — ทำก่อนทุกหมวด อ้างอิง: SECURITY_UPLIFT_DISCUSSION_TOPICS.md หมวด 0 · evidence จาก ROADMAP §3.1

สรุป

3 งานเร่งด่วน: (1) ถอด hardcoded credential + rotate (2) ปิด JWT dev-bypass (3) ปิด endpoint anonymous อันตราย — ทั้งหมดเป็นการ “ปิดรู” ไม่ใช่สร้างของใหม่


งาน 0.1 — ถอด Hardcoded Credential + Rotate

ไฟล์ที่ต้องแก้

#ไฟล์Action
1Backend_NotificationService/src/Notification01.API/Controllers/v1/DiagnosticsController.cs:144ลบ username="admin@exim.go.th", password="Exim@2026!" → อ่านจาก IConfiguration (มาจาก KV) หรือลบ endpoint ทิ้ง (แนะนำ — เป็น diagnostics ที่ไม่ควรอยู่ใน prod)
2DiagnosticsController.cs:138,155endpoint ConnectivityEximToken/ConnectivityEximHeroSlides — ถอด [AllowAnonymous][Authorize(Policy="RequireAdmin")]
3Backend_ThirdPartyService/src/ThirdParty01.API/appsettings.Production.json:9-11ลบค่า ConsumerKey/ConsumerSecret → เหลือ placeholder ว่าง "" แล้วให้ KV inject ผ่าน AddAzureKeyVaultWithServicePrefix

Setup (Key Vault + IaC)

# 1. สร้าง secret ใน KV (ค่าใหม่หลัง rotate — ห้ามใช้ค่าเดิมที่รั่ว)
az keyvault secret set --vault-name <kv-prod> --name "thirdparty-EGovSettings--ConsumerKey"    --value "<NEW>"
az keyvault secret set --vault-name <kv-prod> --name "thirdparty-EGovSettings--ConsumerSecret" --value "<NEW>"
# naming ตาม ServicePrefixKeyVaultSecretManager convention: {service-prefix}-{Section}--{Key}

⚠️ IaC parity (บังคับ): appsettings ที่แก้ต้อง update ที่ IaC config repo (Backend_Iac/config/) ทุก env ที่กระทบด้วย — CI deploy จะ override appsettings ใน image ด้วยค่าจาก IaC เสมอ

ลำดับ rotate (สำคัญ — ทำผิดลำดับ = service ล่ม)

sequenceDiagram
    autonumber
    participant Dev as ทีม Dev
    participant Ext as เจ้าของ credential<br/>(EXIM admin / DGA)
    participant KV as Key Vault
    participant Repo as Git Repo
    participant Pod as Service Pod

    Dev->>Ext: ขอ credential ใหม่ (ยังไม่ revoke ตัวเก่า)
    Ext-->>Dev: credential ใหม่
    Dev->>KV: set secret ค่าใหม่
    Dev->>Repo: commit — ลบ hardcoded, อ่านจาก config/KV
    Dev->>Pod: deploy + restart
    Pod->>KV: โหลด secret ใหม่ตอน startup
    Dev->>Dev: ทดสอบ flow ที่ใช้ credential (token acquisition)
    Dev->>Ext: ยืนยันใช้ค่าใหม่ได้แล้ว → revoke ค่าเก่า
    Note over Repo: ขั้นสุดท้าย (ตัดสินใจทีม): purge git history<br/>ดู IMPL_PLAN_06 §Purge

Verify

  • grep -r "Exim@2026\|ConsumerSecret.*[A-Za-z0-9]\{8,\}" --include="*.cs" --include="*.json" = 0 hit (ยกเว้น placeholder ว่าง)
  • เรียก diagnostics endpoint โดยไม่มี token → ได้ 401/404
  • ThirdParty ขอ DGA token สำเร็จด้วยค่าใหม่จาก KV

งาน 0.2 — ปิด JWT Dev-Bypass (2 แบบ + Template)

สถานะปัจจุบัน vs เป้าหมาย

sequenceDiagram
    autonumber
    participant C as Client (ใครก็ได้บน dev)
    participant S as Backend Service (dev cluster)
    participant E as Entra ID (JWKS)

    rect rgb(255, 230, 230)
    Note over C,S: ปัจจุบัน (แบบ A) — env=Development
    C->>S: token ปลอมรูปร่าง JWT (เซ็นเองก็ได้)
    S->>S: if(isDev) → SignatureValidator passthrough<br/>ValidateIssuer=false, ValidateLifetime=false
    S-->>C: 200 OK — ผ่านหมด ❌
    end

    rect rgb(230, 255, 230)
    Note over C,E: เป้าหมาย — ทุก env รวม dev (ตามแบบ UserService)
    C->>S: token
    S->>E: ดึง signing keys จาก .well-known/jwks (cache)
    S->>S: ValidateIssuer + Audience + Lifetime + Signature
    alt token ถูกต้อง
        S-->>C: 200 OK
    else token ปลอม/หมดอายุ
        S-->>C: 401 Unauthorized ✅
    end
    end

ไฟล์ที่ต้องแก้ (เรียงตามลำดับ)

ลำดับRepo/ไฟล์Action
1Backend_Template/src/SuperApp01.API/.../AuthenticationExtensions.cs:27,44แก้ก่อน — กันของใหม่ติดเชื้อ: if (isDev)if (false) (ตามแบบ Backend_UserService/.../AuthenticationExtensions.cs:38)
2-10แบบ A อีก 9 service: Filter, Log, Consent, Centralized, Codex, FileManagement, Orchestrator, ThirdParty, Task — ไฟล์ AuthenticationExtensions.cs ของแต่ละตัว (บรรทัด ~27-28, 44-45)if (isDev)if (false) — service ละ 1 PR, ทดสอบ E2E บน dev ก่อน merge ตัวถัดไป
11Backend_WorkflowService/src/WorkFlow01.API/Program.cs:73-81 + BypassAuthHandler.csแบบ B: ลบ BypassAuthHandler → port AddAppAuthentication (JWT + AzureAd:AllowedTenants) จาก UserService/Notification ตามที่ comment ในโค้ดระบุไว้เอง
12Backend_ThirdPartyFXService (ทั้ง repo)ไม่มี auth เลย: เพิ่ม AddAppAuthentication + UseAuthentication/UseAuthorization + [Authorize] บน POST counterRates + SignalR hub (ดู hub token ผ่าน query ?access_token= — ลอก pattern จาก NotificationService OnMessageReceived)

💡 ระยะยาว: ย้าย logic นี้เป็น guard กลางใน Backend_Package (ดู IMPL_PLAN_05) — ผูก bypass กับ #if DEBUG build flag ที่ไม่มีวัน true บน cluster image

Prerequisite ก่อนแก้: dev cluster ต้องมี token จริงใช้

dev-bypass ถูกใส่มาเพราะทีม dev ทดสอบสะดวก — ก่อนปิดต้องเตรียมทางเลือก:

  1. ยืนยัน flow ขอ token จริงบน dev ใช้งานได้ (Entra dev tenant / test users)
  2. อัปเดต Postman/Swagger collection ให้มี auth helper
  3. แจ้งทีมล่วงหน้า + กำหนดวัน cutover ต่อ service

Verify

  • Syntactic: grep -rn "if (isDev)" --include="AuthenticationExtensions.cs" = 0 hit ทุก repo · grep -rn "BypassAuthHandler" = 0 hit
  • Functional (ต่อ service): บน dev cluster — token ปลอม → 401 · token จริงจาก Entra → 200 · E2E suite เขียว

งาน 0.3 — ปิด Endpoint Anonymous อันตราย

ไฟล์ปัญหาAction
Backend_NotificationService/.../MaintenanceController.cs:14,17POST toggle เปิด/ปิด maintenance mode ได้โดยไม่ auth[Authorize(Policy="RequireAdmin")] หรือ [RequireApiKey] (ถ้าเรียกจาก ops tooling — ต้อง verify Filters.Add<ApiKeyAuthFilter>() ลงทะเบียนแล้ว)
Notification whoami endpointคืน raw JWT header/payload/claims แบบ anonymous — recon surfaceลบ หรือ [Authorize] + จำกัดข้อมูลที่คืน
Backend_ConsentService/.../ConsentController.cs:14-16ไม่มี attribute เลย — POST /receipt + GET /user?identifier= เปิด anonymousตัดสินใจ end-state ก่อน (D-checklist): user-facing → [Authorize] · service-to-service → [AllowAnonymous]+[RequireApiKey] (⚠️ Consent ยังไม่ register ApiKeyAuthFilter — ต้องเพิ่มใน AddControllers ก่อน มิฉะนั้น attribute เงียบ = no-op)

Verify

ยิงทุก endpoint ข้างบนโดยไม่มี credential → ต้องได้ 401/403 ทุกตัว (สคริปต์ curl เก็บเป็น regression test)


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

  1. ห้าม rotate credential ก่อนมีค่าใหม่พร้อมใช้ — ลำดับตาม sequence diagram งาน 0.1
  2. ห้ามปิด bypass ทุก service พร้อมกัน — ทีละ service, มี rollback plan (revert PR + redeploy)
  3. Consent POST /receipt — เลือกทางเดียว (JWT หรือ API key) ห้ามปล่อยกำกวม
  4. งานนี้ไม่รวม FallbackPolicy — นั่นคือ IMPL_PLAN_05 ซึ่งมีลำดับปลอดภัยของตัวเอง

✅ Definition of Done

  • credential ทั้ง 2 จุดถอด + rotate + KV + IaC parity ครบ
  • dev-bypass = 0 hit ทั้ง platform (แบบ A + B) + ThirdPartyFX มี auth
  • endpoint อันตราย 3 กลุ่มคืน 401/403
  • E2E เขียวบน dev + SIT หลังทุกการเปลี่ยนแปลง

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

  1. หมวดนี้ไม่ควรรอมติประชุม — 0.1 กับ 0.2 (เฉพาะ Template) ไม่มีประเด็นให้ถก ทำได้เลยภายใน 48 ชม. แล้วรายงานผลในที่ประชุมแทน — สิ่งที่ต้องรอคือการ coordinate rotate กับ EXIM/DGA เท่านั้น
  2. DiagnosticsController: ผมเลือกลบ endpoint ทิ้ง ไม่ใช่ย้าย credential ไป KV — connectivity check ควรเป็น health check/synthetic monitor ไม่ใช่ endpoint ที่ถือ credential จริงค้างใน API surface
  3. ลำดับของผม: Template (กันของใหม่ติดเชื้อ) → rotate credential → ปิด endpoint อันตราย (0.3) → dev-bypass ทีละ service — เหตุผล: สามอย่างแรกเสร็จในวันเดียว ส่วน bypass 9 service คืองานยาวที่ต้องมี token จริงบน dev พร้อมก่อน (prerequisite สำคัญกว่าตัวแก้)
  4. อย่าใช้ if (false) เป็น end-state — มันคือ dead code ที่รอคน flip กลับ · ใช้เป็น hotfix ชั่วคราวได้ แต่ end-state ต้องเป็น guard กลาง #if DEBUG ใน Package (IMPL_PLAN_05) — ลบ if (false) ทิ้งตอน rollout helper
  5. สิ่งที่คนมักพลาด: rotate แล้วลืมตรวจว่าค่าเก่าถูก revoke จริง — rotate โดยไม่ revoke = ค่าที่รั่วยังใช้ได้ เท่ากับไม่ได้ rotate — ใส่ “ยืนยัน revoke แล้ว” เป็น checkbox สุดท้ายเสมอ

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

หลักการแหล่งอ้างอิง
Hardcoded credential / auth bypass = Broken AuthenticationOWASP API2
— Broken Authentication
วิธีจัดการ secret ที่รั่วแล้ว (rotate + ถือว่า compromised)OWASP Secrets Management Cheat Sheet
Purge secret ออกจาก git historyGitHub Docs — Removing sensitive data from a repository
Endpoint anonymous/misconfig ที่เปิดค้างOWASP API8
— Security Misconfiguration
เก็บ secret ใน vault แทน config/sourceAzure Key Vault — Best practices