IMPL PLAN 07 — Redis Hardening
Redis คือจุดเสี่ยงสูงสุดของระบบ (ถือ session/token ทุก user) — เข้ารหัสข้อมูล, จำกัด blast radius, ตรวจ transport/access
อัปเดต: 2026-07-07
IMPL PLAN 07 — Redis Hardening (Session Store + Cache)
วันที่: 07/07/2026 · สถานะ: ⏳ รอ verify · Priority: 🟡 Phase 2 อ้างอิง: DISCUSSION หมวด 7 · SG-11 + ROADMAP §3.2 (PII plaintext ใน Redis)
สรุป
Redis คือจุดรวมความเสี่ยงสูงสุดตัวเดียวของระบบ — ถือ session + token ทุก user (Sentinel) + user info cache (RedisUserInfoMiddleware ใน Package) — ถ้า Redis หลุด = hijack ได้ทั้งระบบ · งาน 4 เรื่อง: เข้ารหัสของใน Redis, ตรวจ transport/access, จำกัด blast radius, ย้าย state ที่ควรอยู่ Redis
Sequence Diagram — Data ใน Redis: ปัจจุบัน vs เป้าหมาย
sequenceDiagram
autonumber
participant SG as Sentinel
participant MW as RedisUserInfoMiddleware<br/>(Backend_Package)
participant R as Azure Redis Premium
rect rgb(255,235,235)
Note over SG,R: ปัจจุบัน
SG->>R: SET session:{id} → { accessToken, refreshToken } ❌ plaintext (SG-11 code มี key ยังไม่ provision)
MW->>R: SET userinfo:{sub} → PII (ชื่อ, email, ...) ❌ plaintext
end
rect rgb(235,255,235)
Note over SG,R: เป้าหมาย
SG->>SG: encrypt(token, AES key จาก KV) ก่อนเขียน
SG->>R: SET session:{id} → ciphertext ✅
MW->>MW: ลด field: เก็บเฉพาะที่ downstream ใช้จริง (minimization)
MW->>R: SET userinfo:{sub} → เฉพาะ field จำเป็น + field อ่อนไหว encrypt ✅ + TTL สั้น
end
งาน 7.1 — Token At-Rest Encryption (SG-11)
= งาน H2 ใน IMPL_PLAN_04 — provision key ใน KV + เปิด flag + migration plan (รองรับอ่าน 2 แบบชั่วคราว หรือ invalidate ทุก session)
งาน 7.2 — PII ใน User Info Cache
| # | จุด | Action |
|---|---|---|
| 1 | Backend_Package/src/Middleware/RedisUserInfo/UserInfoForRedis.cs | audit field ทั้งหมด — ตัด field ที่ไม่มี consumer ใช้ (minimization ก่อน encryption) |
| 2 | RedisUserInfoMiddleware.cs + RedisUserInfoServiceExtensions.cs | field อ่อนไหวที่เหลือ (เลขบัตร ฯลฯ ถ้ามี) → encrypt ด้วย IEncryptionService ก่อน serialize |
| 3 | RedisUserInfoOptions.cs | ตรวจ TTL — cache PII ควรสั้น (เสนอ ≤ 15 นาที) ไม่ใช่ตาม session |
งาน 7.3 — Transport + Access (verify จาก IaC)
Checklist ตรวจใน Backend_Iac + Azure portal (แล้ว snapshot ผลเป็นหลักฐาน):
- TLS only:
enableNonSslPort=false+ client ทุกตัวต่อ port 6380 (ssl=trueใน connection string) - ไม่มี public endpoint — private endpoint / VNet injected เท่านั้น + firewall rules
- AUTH ด้วย access key จาก KV (หรือ Entra ID auth ถ้า tier รองรับ) — ตรวจว่า connection string ไม่โผล่ใน manifest plaintext (ต้องมาจาก SecretProviderClass)
- แยก Redis (หรืออย่างน้อยแยก DB index + key prefix) ระหว่าง session store ของ Sentinel กับ cache ทั่วไปของ services — จำกัด blast radius: service ธรรมดาที่ถูก compromise ไม่ควรอ่าน session ทั้งระบบได้
-
maxmemory-policyของ session store =noeviction(session โดน evict = user หลุดแบบสุ่ม + เสี่ยง fail-open path)
งาน 7.4 — State ที่ควรอยู่ Redis (จาก in-memory)
จาก [ROADMAP §3.1 #5/M4]: ถ้าอนาคตทำ rate-limit escalation (penalty box) — state ต้องอยู่ Redis ไม่ใช่ in-memory per-pod (AKS หลาย replica = state ไม่ consistent) · Idempotency store ของ IdempotencyFilter ตรวจว่าใช้ distributed cache แล้วหรือ in-memory
Verify
-
redis-cli --scan --pattern 'session:*'+ GET ตัวอย่าง → ciphertext - userinfo cache มีเฉพาะ field ตาม audit + TTL ≤ ค่าที่ตกลง
- เชื่อมต่อ non-TLS ถูกปฏิเสธ · ต่อจากนอก VNet ไม่ได้
- ตาราง “ใครต่อ Redis ไหน ด้วยสิทธิ์อะไร” เสร็จเป็นหลักฐาน
⚠️ ข้อควรระวัง
- เข้ารหัส/ลด field ใน userinfo cache = breaking change ต่อ consumer ของ middleware — ตรวจทุก service ที่อ่าน
UserInfoForRedisก่อน bump Package noeviction+ memory เต็ม = write fail — ต้องมี monitoring memory + alert คู่กัน- Redis Premium มี persistence (RDB/AOF) — ถ้าเปิดอยู่ ไฟล์ backup ก็มี token/PII → at-rest encryption ของ 7.1/7.2 ครอบถึง backup โดยอัตโนมัติ (เข้ารหัสก่อนเขียน) — นี่คือเหตุผลที่ encrypt ฝั่ง app ดีกว่าพึ่ง disk encryption อย่างเดียว
🎯 ถ้าเป็นผมจะทำอย่างไร (ความเห็น Fable)
- 7.3 (verify transport/access) ทำก่อนเพราะเป็น read-only — ถ้าพบ Redis มี public endpoint หรือ non-SSL port เปิด นั่นคือไฟไหม้จริงที่ต้องดับก่อน encryption ทั้งหมด — ตรวจ 15 นาทีรู้ผล
- 7.2: minimization ก่อน encryption เสมอ — field ที่ตัดออกได้ไม่ต้อง encrypt · ผมคาดว่า audit จะพบว่า consumer ใช้จริงไม่กี่ field — เหลือน้อยจน encryption อาจไม่จำเป็นเลย
- การแยก session store ออกจาก cache ทั่วไป (blast radius) คือข้อที่คุ้มที่สุดในเล่มนี้ — เริ่มจาก DB index + key prefix + ACL ก่อน (ถูก) ค่อยแยก instance จริงเมื่อมีงบ — service ธรรมดาโดน compromise ไม่ควรอ่าน session ทั้งระบบได้
- 7.1 ทำพร้อม H2 ของ PLAN 04 เป็นงานเดียว — อย่าแยก track กันแล้วทำซ้ำ/ชนกัน
- ข้อ 7.4 (penalty state ใน Redis) — ยังไม่ทำ จนกว่าจะมี enforcement design จริง (ROADMAP M4) — อย่าสร้าง state ที่ไม่มี consumer
📚 อ้างอิงมาตรฐาน (สำหรับใช้ในที่ประชุม):
| หลักการ | แหล่งอ้างอิง |
|---|---|
| Redis security model (AUTH/ACL, TLS, network isolation) — official | Redis Documentation — Security |
| Private endpoint / ปิด public access ของ Azure Redis (official) | Microsoft Learn — Azure Cache for Redis network isolation |
| Session store คือ asset วิกฤต — การปกป้อง session data | OWASP Session Management Cheat Sheet |
| ทำไม token/PII at-rest ต้องเข้ารหัสแม้อยู่หลัง firewall | OWASP A02 — Cryptographic Failures |