IMPL PLAN 04 — Authorization Flow Hardening
Hardening 6 จุดของ flow Entra → Sentinel → APIM → JWT ที่มีอยู่แล้ว: session TTL, token at-rest, realm selector, replay protection
อัปเดต: 2026-07-07
IMPL PLAN 04 — Authorization Flow Hardening (Sentinel / Session / Token)
วันที่: 07/07/2026 · สถานะ: ⏳ รอ verify · Priority: 🟡 Phase 2 อ้างอิง: DISCUSSION หมวด 4 · SG-05/06/11/22 จาก COMPLIANCE
สรุป
Flow หลัก (Entra → Sentinel cookie → APIM façade → JWT) มีครบและถูกต้องแล้ว — งานหมวดนี้คือ hardening 6 จุดของสิ่งที่มี: session TTL, token at-rest encryption, InternalOnly gate, SameSite, realm selector, replay protection
Sequence Diagram — Flow ปัจจุบัน + จุดที่ harden (ตัวเลข H1-H6)
sequenceDiagram
autonumber
participant B as Browser
participant SG as Sentinel Gateway
participant E as Entra ID / CIAM
participant R as Redis (session store)
participant APIM as APIM
participant BE as Backend
Note over B,E: ① Login (OIDC PKCE S256 — มีแล้ว)
B->>SG: GET /sentinel/login
SG->>E: redirect authorize (PKCE + state)
E-->>SG: callback + code
SG->>E: แลก code → access/refresh token
SG->>R: เก็บ token server-side ผูก session<br/>🔧 H2: เข้ารหัส token at-rest (code มีแล้ว SG-11 — provision key + เปิด)
SG-->>B: Set-Cookie .Exim.Auth.{tenant} (HttpOnly, Secure)<br/>🔧 H4: ทบทวน SameSite=None บน Dev/Sit/Uat (SG-22)<br/>🔧 H1: idle/absolute cap — ตั้งค่า (code มีแล้ว SG-05)
Note over B,BE: ② เรียก API (session façade — มีแล้ว)
B->>APIM: request + cookie
APIM->>SG: POST /sentinel/internal/resolve-session<br/>🔧 H3: verify InternalAccess.Enabled=true ทุก env (SG-06)
SG->>R: lookup session → token<br/>🔧 H1: เช็ค idle timeout ที่นี่
SG-->>APIM: JWT
APIM->>BE: forward + JWT
BE-->>B: response
Note over B,SG: ③ กรณีไม่มี Origin header (dual-cookie)
B->>SG: request ไม่มี Origin
SG->>SG: 🔧 H5: realm selector — ตอนนี้เลือก realm แรกเสมอ<br/>→ fix: เลือกจาก ?tenant= / cookie ล่าสุด
Note over B,BE: ④ High-risk operation
B->>APIM: POST /transfer (สมมติ)
Note over APIM,BE: 🔧 H6: replay protection — ประเมิน nonce/idempotency key<br/>(IdempotencyFilter มีใน Package แล้ว — ใช้เป็นกลไก)
งาน H1 — Session TTL (idle + absolute cap)
สถานะ: code รองรับแล้ว (SG-05 แก้ 04/06/2026) — ค่ายังไม่ตั้งใน config ทุก env
| # | จุด | Action |
|---|---|---|
| 1 | Backend_SentinelGatewayService appsettings + IaC parity ทุก env | ตั้งค่า session cap — เสนอ: IdleTimeout: 30m (ผู้ใช้ทั่วไป), AbsoluteLifetime: 8h, RememberMe: idle 7d / absolute 30d → ตัวเลขจริง = decision D5 ของที่ประชุม |
| 2 | ทดสอบ UX | idle 30 นาทีบน mobile app = เด้ง login บ่อยไหม — เทสกับทีม UX ก่อน production |
Verify: ปล่อย session idle เกินกำหนด → เรียก API ได้ 401 + redirect login · session อายุเกิน absolute → เหมือนกันแม้ active
งาน H2 — Token At-Rest Encryption ใน Redis
สถานะ: code มีแล้ว (SG-11) — key ยังไม่ provision
| # | จุด | Action |
|---|---|---|
| 1 | KV ทุก env | provision key: sentinel-TokenEncryption--Key (32B Base64) |
| 2 | Sentinel config + IaC | เปิด flag เข้ารหัส token ก่อนเขียน Redis |
| 3 | Migration | session เดิมใน Redis เป็น plaintext — เลือก: (ก) รองรับอ่านทั้ง 2 แบบชั่วคราว หรือ (ข) invalidate ทุก session ตอน deploy (user login ใหม่ — ประกาศ maintenance window) |
Verify: redis-cli GET <session-key> → เห็น ciphertext ไม่เห็น token plaintext
งาน H3 — InternalOnly Gate
สถานะ: InternalOnlyAttribute default Enabled=false + ไม่พบ config ใน repo (SG-06) = อาจปิดอยู่บน env จริงโดยไม่รู้ตัว
| # | จุด | Action |
|---|---|---|
| 1 | Backend_Iac config ทุก env | ตรวจ/เพิ่ม InternalAccess:Enabled=true + AllowedCidrs (subnet ของ APIM) + ApiKey สำหรับ resolve-session |
| 2 | Sentinel startup | เพิ่ม fail-fast: ถ้า production + Enabled=false หรือ CIDR/ApiKey ว่าง → throw ตอน boot (กัน misconfig เงียบ — pattern เดียวกับ SG-19) |
Verify: เรียก /sentinel/internal/resolve-session จากนอก CIDR → 403 · จาก APIM → ปกติ
งาน H4 — SameSite ต่อ env
ทบทวน SameSite=None บน Dev/Sit/Uat (SG-22) → ถ้า FE/Sentinel อยู่ same-site จริง เปลี่ยนเป็น Lax — ตรวจ cross-origin flow (LIFF/WebView) ก่อน
งาน H5 — Realm Selector (dual-cookie ไม่มี Origin)
AuthenticationExtensions.cs:54-62 (Sentinel) เลือก realm แรกตาม DisplayOrder เสมอเมื่อไม่มี Origin → fix: รับ hint ?tenant= + fallback เป็น cookie ที่ issue ล่าสุด — เขียน unit test ครอบ 3 เคส (Origin มี / ไม่มี+tenant hint / ไม่มีทั้งคู่)
งาน H6 — Replay Protection สำหรับ High-Risk Ops
- กลไกมีแล้ว:
IdempotencyFilter+IdempotentAttributeใน Package (UserService register แล้ว) - งาน: กำหนด list endpoint high-risk (write ที่มีผลเงิน/สถานะสำคัญ) → บังคับ
[Idempotent]+ client ส่งIdempotency-Keyheader → ประเมินว่าพอสำหรับ API-GL-001 §7(2) หรือต้องเพิ่ม nonce แบบ one-time (คุยกับ compliance)
Verify รวม
- ตาราง session TTL ต่อ env ตั้งครบ + ทดสอบ idle/absolute
- Redis ไม่มี token plaintext
- resolve-session ถูก gate ด้วย CIDR+ApiKey ทุก env + fail-fast ตอน boot
- dual-cookie เลือก realm ถูกทั้ง 3 เคส
- endpoint high-risk มี idempotency บังคับ
⚠️ ข้อควรระวัง
- H2 migration — เลือกทาง (ข) invalidate ต้องประกาศก่อน (ทุก user หลุด login)
- H3 fail-fast — ทดสอบบน dev ก่อน: ถ้า IaC ยังไม่มีค่า deploy จะ boot fail ทันที (by design แต่ต้องเตรียมค่าให้ครบก่อน merge)
- H1 กระทบ UX ตรงที่สุด — ให้ product owner ร่วมตัดสิน ไม่ใช่ทีม dev ตัดสินเอง
🎯 ถ้าเป็นผมจะทำอย่างไร (ความเห็น Fable)
- D5 ที่ผมจะเสนอ: idle 30 นาที / absolute 12 ชม. / RememberMe idle 7 วัน absolute 30 วัน — 30 วันเดิมไม่ผิดในตัวมันเองถ้ามี idle cap คู่กัน — ปัญหาคือตอนนี้ไม่มี idle cap เลย
- H2 migration เลือก (ข) invalidate ทุก session — dual-read เพิ่ม code path ชั่วคราวที่มักกลายเป็นถาวร · การให้ user login ใหม่ 1 ครั้งพร้อมประกาศล่วงหน้าคือราคาที่ถูกกว่ามาก — ทำนอก peak hours
- H3 fail-fast = ทำเลยไม่ต้องถาม — InternalOnly gate ที่อาจปิดอยู่โดยไม่รู้ตัวคือ resolve-session เปิดโล่ง = ใครก็แลก cookie เป็น JWT ได้ — นี่คืองานแรกของหมวดนี้ที่ผมจะทำ (verify ค่า env จริงก่อนอย่างอื่น)
- H6 อย่า over-engineer — ใช้ IdempotencyFilter ที่มี + JWT lifetime สั้น พอสำหรับ scope ปัจจุบัน (ยังไม่มี endpoint โอนเงิน) · nonce infra เต็มรูปแบบค่อยทำเมื่อมี use case จริง
- ลำดับของผม: H3 (เสี่ยงสุด+ถูกสุด) → H1 (ตั้ง config) → H2 (provision key) → H5 → H4 → H6 — H3 เป็น verify + fail-fast ไม่ใช่ feature ใหม่
📚 อ้างอิงมาตรฐาน (สำหรับใช้ในที่ประชุม):
| หลักการ | แหล่งอ้างอิง |
|---|---|
| Session timeout (idle + absolute) ตามระดับ assurance | NIST SP 800-63B — Digital Identity Guidelines: Authentication & Lifecycle |
| Session lifecycle, cookie attributes, TTL best practices | OWASP Session Management Cheat Sheet |
| OAuth 2.0 security hardening (token handling, replay) | RFC 9700 — Best Current Practice for OAuth 2.0 Security |
| SameSite + CSRF compensating controls | OWASP CSRF Prevention Cheat Sheet |
| Token ใน session store ฝั่ง server (BFF pattern — ยืนยัน architecture ปัจจุบันถูกทาง) | IETF — OAuth 2.0 for Browser-Based Applications (draft BCP) |