Skip to content

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)

RepoFinding ที่เกิดจาก pattern นี้
cu-central-apiSEC-CU-03
centralize-apiCRIT-01
vtrc-rc-backofficeC1
benefit-apiSEC-BENEFIT-01
recruitment-apiSEC-RECRUIT-01
ai-recruitment-apiSEC-AIRECRUIT-01
pms-apiSEC-PMS-02
chat-center-apiSEC-CHAT-04
job-scheduler-apiSEC-JOBSCHED-01
vtrc-commonSEC-COMMON-03
workflow-apiSEC-WORKFLOW-02
meeting-vtrc-apiSEC-MTVTRC-01
recruitment-webSEC-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)

RepoFinding
centralize-apiCRIT-03 (100% ของ endpoint)
2way-apiSEC-2WAYAPI-04
2way-line-serviceSEC-2WAYLINE-01 (100% ของ endpoint)
2way-vtrc-apiSEC-2WAYVTRC-02
pms-apiSEC-PMS-01
vtrc-commonSEC-COMMON-01
meeting-vtrc-apiSEC-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)

RepoFinding
2way-apiSEC-2WAYAPI-03
benefit-apiSEC-BENEFIT-02
recruitment-apiSEC-RECRUIT-02
chat-center-apiSEC-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)

RepoFinding
2way-apiSEC-2WAYAPI-01
2way-vtrc-apiSEC-2WAYVTRC-01
cu-central-apiSEC-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)

RepoFindingรูปแบบ PII exposure
centralize-apiCRIT-04endpoint return PII ไม่มี auth
2way-vtrc-apiSEC-2WAYVTRC-02endpoint return PII จาก lineID ไม่ auth
pms-apiSEC-PMS-01endpoint return เงินเดือนไม่ auth
meeting-vtrc-apiSEC-MTVTRC-02endpoint mock ที่ return PII จริง hardcode
report-logSEC-REPORTLOG-01log 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 1canonical-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 🔴


ลำดับความสำคัญที่แนะนำ (ระดับแพลตฟอร์ม ไม่ใช่รายจุด)

  1. Purge .env จาก git history ทุก repo ที่อยู่ใน Pattern A พร้อมกัน — ทำเป็น batch operation เดียวเพราะเป็น root cause เดียวกัน ไม่ใช่ 13 ปัญหาแยกกัน
  2. Rotate secret ทุกตัวใน 5 cluster ของ Volume 11.4 พร้อมกัน — rotate ทีละ repo จะทำให้ service อื่นใน cluster เดียวกัน verify token/DB ไม่ผ่านทันที ต้องวางแผน coordinated rotation
  3. เขียน guideline กลางสำหรับ @Public()/auth decision แล้ว retrofit เข้า 7 repo ของ Pattern B
  4. ลบ backdoor 3 จุดของ Pattern D ทันที — ผลกระทบต่อ business logic น้อยที่สุดเทียบกับ effort (ใช้เวลา < 1 วันต่อจุดตามที่ scorecard ต้นทางประเมินไว้)
  5. แก้ SQL injection 4 จุดของ Pattern C — มี parameterized API พร้อมใช้แล้วในโค้ดเดียวกัน ไม่ต้องออกแบบใหม่
  6. แก้ตัวเลขใน 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 เดียวกัน