12.4 · Scorecard — systemic security patterns
บทนี้ไม่ list finding รายตัวซ้ำ (อยู่ที่ 12.3 แล้ว) แต่จัดกลุ่ม 48 Critical finding ตาม pattern เชิงระบบที่ทำให้เกิด finding เหล่านั้น เพื่อให้ remediation ทำเป็นชุดได้ (fix pattern เดียวแก้ได้หลาย repo พร้อมกัน) แทนการไล่แก้ทีละ finding
Pattern A — .env/.env.* production commit เข้า git (root cause ของ 13 finding จาก 48)
| Repo | Finding ที่เกิดจาก pattern นี้ |
|---|---|
cu-central-api | SEC-CU-03 |
centralize-api | CRIT-01 |
vtrc-rc-backoffice | C1 |
benefit-api | SEC-BENEFIT-01 |
recruitment-api | SEC-RECRUIT-01 |
ai-recruitment-api | SEC-AIRECRUIT-01 |
pms-api | SEC-PMS-02 |
chat-center-api | SEC-CHAT-04 |
job-scheduler-api | SEC-JOBSCHED-01 |
vtrc-common | SEC-COMMON-03 |
workflow-api | SEC-WORKFLOW-02 |
meeting-vtrc-api | SEC-MTVTRC-01 |
recruitment-web | SEC-RCWEB-01 |
13 จาก 21 repo ที่มี Critical finding (62%) มี pattern นี้เป็นสาเหตุตรง — เป็น pattern เดียวที่ ROI สูงสุดถ้าจะแก้ทีเดียวทั้งแพลตฟอร์ม: เพิ่ม .env เข้า .gitignore ทุก repo, purge จาก git history (git filter-repo), ย้ายเข้า secret manager กลาง (Vault/k8s Secret/GCP Secret Manager — meeting-vtrc-api เป็น repo เดียวที่มี comment แนะนำ GCP Secret Manager ไว้แล้วในเอกสาร remediation ของตัวเอง)
Pattern B — @Public()/ไม่มี auth guard บน endpoint ที่ควร private (root cause ของ 7 finding)
| Repo | Finding |
|---|---|
centralize-api | CRIT-03 (100% ของ endpoint) |
2way-api | SEC-2WAYAPI-04 |
2way-line-service | SEC-2WAYLINE-01 (100% ของ endpoint) |
2way-vtrc-api | SEC-2WAYVTRC-02 |
pms-api | SEC-PMS-01 |
vtrc-common | SEC-COMMON-01 |
meeting-vtrc-api | SEC-MTVTRC-02 |
Root cause ทางองค์กร — ไม่มี guideline กลางว่า endpoint แบบไหนควร @Public() ทีมแต่ละ repo ตัดสินใจเองจาก deadline/convenience ไม่ใช่ threat model แนะนำให้เขียน decision checklist กลาง (เช่น "endpoint ที่ return PII หรือข้อมูลการเงินต้อง require JWT เสมอ ไม่มีข้อยกเว้น") แล้วบังคับผ่าน code review
Pattern C — Raw SQL string interpolation → SQL injection (root cause ของ 4 finding ระดับ Critical)
| Repo | Finding |
|---|---|
2way-api | SEC-2WAYAPI-03 |
benefit-api | SEC-BENEFIT-02 |
recruitment-api | SEC-RECRUIT-02 |
chat-center-api | SEC-CHAT-01 |
ทุกจุดมี TypeORM parameterized query API ให้ใช้อยู่แล้วในเฟรมเวิร์กเดียวกัน (entityManager.query(sql, params), QueryBuilder.setParameter()) — จุดที่เกิด SQL injection ทุกจุดคือข้อยกเว้นที่หลุดออกจาก pattern ที่ถูกต้องซึ่งมีอยู่แล้วในไฟล์เดียวกัน ไม่ใช่ทีมไม่รู้จัก parameterized query (ยืนยันจาก vtrc-common/04-scorecard.md's SEC-COMMON-02 ที่ระบุตรงว่า "เมธอดอื่นในไฟล์เดียวกันใช้ .setParameter() อยู่แล้ว เป็นตัวอย่างที่ถูกต้อง")
Pattern D — Hardcoded backdoor/bypass credential ที่ทำงานได้ใน production build (root cause ของ 3 finding)
| Repo | Finding |
|---|---|
2way-api | SEC-2WAYAPI-01 |
2way-vtrc-api | SEC-2WAYVTRC-01 |
cu-central-api | SEC-CU-04 |
ทุกจุดไม่มี environment check/feature flag แยก debug code ออกจาก production build — แก้ด้วย build-time flag (เช่น NODE_ENV !== 'production') ครอบโค้ด backdoor ทุกจุด หรือลบทิ้งแล้วย้ายไป seed data ของ test environment เท่านั้น
Pattern E — Shared secret ข้าม service (root cause ของ finding ที่ขยาย blast radius ของ Pattern A)
รายละเอียดเต็มอยู่ที่ Volume 11.4 — 5 cluster ครอบ 14 repo ทำให้ Pattern A (secret หลุด 1 ไฟล์) ขยายผลกระทบเป็นวงกว้างกว่าที่ scorecard รายตัวประเมินไว้ (เพราะแต่ละ scorecard ประเมิน severity จาก muvo ของ repo ตัวเองเท่านั้น ไม่เห็น cluster)
Pattern F — RSA private key hardcode ที่ compile เข้า client bundle (root cause ของ 2 finding, 1 key)
vtrc-mobile (SEC-MOBILE-01) และ new-vtrc-mobile (SEC-NEW-01) ใช้ RSA private key เดียวกัน — แก้ที่ต้นทาง (ออกแบบใหม่ให้ private key อยู่ที่ server เท่านั้น) แก้ปัญหาทั้ง 2 finding พร้อมกัน แต่ต้อง coordinate rebuild + force-update ทั้งสองแอปพร้อมกันเพราะ key ฝัง binary ไม่ใช่ config
Pattern G — PII ถูก return/commit โดยไม่มีการป้องกัน (root cause ของ ≥ 5 finding)
| Repo | Finding | รูปแบบ PII exposure |
|---|---|---|
centralize-api | CRIT-04 | endpoint return PII ไม่มี auth |
2way-vtrc-api | SEC-2WAYVTRC-02 | endpoint return PII จาก lineID ไม่ auth |
pms-api | SEC-PMS-01 | endpoint return เงินเดือนไม่ auth |
meeting-vtrc-api | SEC-MTVTRC-02 | endpoint mock ที่ return PII จริง hardcode |
report-log | SEC-REPORTLOG-01 | log file ที่มี PII จริง commit เข้า git |
Meta-debt — documentation/aggregation gap ที่พบระหว่างสังเคราะห์ (ไม่ใช่ security finding ของ repo ใด แต่เป็น debt ของตัว manual เอง)
QUAL-SEC-01 · Volume 6 index.md นับยอด Critical ของ benefit-api ผิด (3 vs 2 ที่ยืนยันได้จาก scorecard)
ที่ — docs/volume-06-domain-microservices/index.md ตาราง "Scorecard สรุปรวม" เทียบกับ benefit-api/06-scorecard.md — รายละเอียดเต็มที่ 12.3 ท้ายตาราง ควรแก้ตัวเลขใน Volume 6 index.md จาก 16 เป็น 15 (รวม) และจาก 3 เป็น 2 (benefit-api)
CORR-SEC-02 · vtrc-api ไม่ถูกนับเข้า JWT-secret blast radius ใน canonical-truths.md
ที่ — รายละเอียดเต็มที่ Volume 11.4 Cluster 1 — canonical-truths.md/Volume 6 index.md ระบุ shared JWT secret ครอบ 4 domain microservice แต่หลักฐานจาก recruitment-api/04-auth-integrations.md ยืนยันว่า vtrc-api (legacy core) อยู่ใน cluster เดียวกันด้วย ทำให้ scope จริงคือ 5 service
Scorecard สรุป
| Dimension | สถานะ | หมายเหตุ |
|---|---|---|
| Secret management | 🔴 | ไม่มี repo ใดใช้ secret manager — 13/21 repo ที่มี Critical finding มาจาก .env commit |
| Endpoint authorization consistency | 🔴 | 7 repo มี endpoint sensitive ที่หลุดเป็น public โดยไม่มี guideline กลาง |
| SQL injection defense | 🟡 | มี parameterized query pattern ที่ถูกต้องอยู่แล้วในเฟรมเวิร์ก แต่ 4 repo มีจุดที่หลุดไม่ใช้ |
| Debug/backdoor code hygiene | 🔴 | 3 repo มี backdoor ที่ทำงานได้จริงใน production build |
| Cross-service secret isolation | 🔴 | 5 secret cluster ครอบ 14 repo — ไม่มี secret ใดเป็นของ service เดียวจริง |
| Mobile client-side secret storage | 🔴 | RSA private key เดียวกันฝัง 2 app bundle |
| PII exposure control | 🔴 | ≥ 5 finding ที่ PII หลุดจาก endpoint หรือ log โดยตรง |
| Documentation ↔ evidence consistency (meta) | 🟡 | พบ 2 จุดที่เอกสารรวม (canonical-truths.md/Volume 6 index.md) นับ scope ไม่ตรงกับ evidence รายไฟล์ |
คะแนนรวม: 0 🟢 / 1 🟡 / 7 🔴
ลำดับความสำคัญที่แนะนำ (ระดับแพลตฟอร์ม ไม่ใช่รายจุด)
- Purge
.envจาก git history ทุก repo ที่อยู่ใน Pattern A พร้อมกัน — ทำเป็น batch operation เดียวเพราะเป็น root cause เดียวกัน ไม่ใช่ 13 ปัญหาแยกกัน - Rotate secret ทุกตัวใน 5 cluster ของ Volume 11.4 พร้อมกัน — rotate ทีละ repo จะทำให้ service อื่นใน cluster เดียวกัน verify token/DB ไม่ผ่านทันที ต้องวางแผน coordinated rotation
- เขียน guideline กลางสำหรับ
@Public()/auth decision แล้ว retrofit เข้า 7 repo ของ Pattern B - ลบ backdoor 3 จุดของ Pattern D ทันที — ผลกระทบต่อ business logic น้อยที่สุดเทียบกับ effort (ใช้เวลา < 1 วันต่อจุดตามที่ scorecard ต้นทางประเมินไว้)
- แก้ SQL injection 4 จุดของ Pattern C — มี parameterized API พร้อมใช้แล้วในโค้ดเดียวกัน ไม่ต้องออกแบบใหม่
- แก้ตัวเลขใน
canonical-truths.md/Volume 6 index.mdตามQUAL-SEC-01/CORR-SEC-02— effort ต่ำ แต่จำเป็นก่อนใช้เอกสารเหล่านี้ตัดสินใจ incident response scope
กลับไป Volume 12 · index หรือดู Volume 11 · Data Model & Persistence สำหรับ data-model lens ของ evidence เดียวกัน