Skip to content

10.11 · Scorecard + remediation

บทสุดท้ายของ Volume 10 สรุป severity ทั้งหมด จัด priority remediation เป็น 4 wave และเชื่อมไปยัง modernization target ใน Volume 15


Severity summary

SeverityCountIDs
🔴 Critical14SEC-1 (×5 endpoints), SEC-2 (webdeployment), SEC-3 (Conicle SSRF), SEC-4 (statLog SQLi), SEC-5 (GCP key), SEC-6 (prod device keys), SEC-7 (centralize .env), SEC-8 (cu-central .env), SEC-9 (REACT_APP bundle), SEC-10 (no rate limit), CORR-4 (RBAC), CORR-6 (syncModel), LEG-1 (Node 12)
🟡 High12CORR-2 (txn swallow), PERF-3 (JSON scan), PERF-4 (N+1 RBAC), PERF-7 (phpMyAdmin), PERF-8 (network_mode), SEC-6 (MSSQL trust), LEG-3/4 (Apollo/React EOL), LEG-9 (jsonwebtoken), LEG-10 (PDF stack), LEG-12 (redis v3), LEG-15 (no lockfile)
🟢 Medium8CORR-7/8, LEG-11/13/14/17/18/24/25

Scorecard ตาม dimension

DimensionCurrent StateAfter Wave 0-1After Wave 2-3 (target)
Authentication🔴 refresh decode ไม่ verify, JWT 'secret' default, no rate limit🟡 verify + hard-fail + rate limit🟢 signed refresh token rotation + lockout + MFA option
Authorization🔴 CORR-4 RBAC inconsistency, no default-deny🟡 Casbin policy + test matrix🟢 typed RoleGrant + cached permission set
Credential hygiene🔴 GCP key + prod device keys + .env in repo🟡 rotated + secret manager🟢 short-lived tokens + KMS-backed
Injection🔴 execSync × 5 + /webdeployment + statLog SQLi🟡 arg-array + parameterized SQL🟢 no shell-out in handlers + sandboxed renderer
File handling🔴 multer no filter + path traversal🟡 fileFilter + normalize🟢 object storage + signed URL
Network🔴 CORS *, phpMyAdmin exposed, network_mode host🟡 allow-list + bastion DB🟢 mTLS internal + zero-trust
Logging🔴 refreshToken + forceLoging injection🟡 redaction + structured🟢 PII denylist + sampled
Supply chain🔴 Node 12, axois, jsonwebtoken@8🟡 Node 20 + clean deps🟢 Go modules + pinned + SBOM
Runtime🔴 introspection/playground prod🟡 gated by IS_PRODUCTION🟢 schema registry + rate limit + WAF

Legend

  • 🔴 Critical = active risk ต้องแก้ทันที
  • 🟡 High = มี impact จริงแต่ต้องมีเงื่อนไข
  • 🟢 Medium = hygiene/debt ยังไม่อันตรายทันที

Wave 0 — Stop-the-bleed (Weeks 0-2)

Quick wins ที่ลบ Critical risk โดยไม่ต้อง refactor architecture

#ActionDebt IDsEffortChapter
1Rotate GCP SA key + purge from git historySEC-111d9.5
2Rotate prod device keys (ทั้ง 7) + ย้ายไป envSEC-31-2d9.5
3Hard-fail at boot ถ้า JWT_SECRET missingSEC-40.5d9.5
4Gate playground/introspection on !IS_PRODUCTIONSEC-20.5d9.10
5แก้ CORS typo + ลบ default cors()SEC-5 (vtrc-api)0.5d9.8
6centralize-api CORS allow-list แทน *SEC-50.5d9.8
7Replace execSync(qpdf)execFile arg-arraySEC-1, PERF-12-3d9.6
8ลบ /webdeployment endpointSEC-2 (webdeploy)0.5d9.6
9Parameterize statLog.js SQLSEC-4 (SQLi)1d9.6
10Remove phpMyAdmin from prod composePERF-70.5d9.8
11Disable syncModelToTable() in prodCORR-60.5d(Vol 8.4)
12Purge typosquat/unused packages (axois, s, stream, findit)SEC-9, LEG-131d9.10
13Rotate centralize-api DB password + ลบ .envSEC-71d9.5
14Rotate cu-central-api credentials + ลบ .env.develop-testSEC-81-2d9.5
15Rotate MSSQL sa password + ย้ายออกจาก REACT_APP_*SEC-91d9.5
16network_mode: host → bridge + port mappingPERF-80.5d9.8

รวม Wave 0 ≈ 2-3 สัปดาห์ — ลด Critical จาก 14 เหลือ 0


Wave 1 — Stabilize (Months 1-3)

Interim modernize บน Node.js stack ปัจจุบัน

#ActionDebt IDsEffortChapter
1Bump Node 12 → 20 LTS + ลบ esm shimLEG-1, LEG-22-3 wk9.10
2Native ESM ("type": "module")LEG-21 wk9.10
3Upgrade jsonwebtoken v9 + add algorithms: ['HS256']LEG-9, SEC (decode-vs-verify)1 wk9.3, 9.10
4Upgrade redis v4, multer v2, axios v1LEG-12, LEG-201-2 wk9.7, 9.10
5Add rate limit + structured logger + Redis session-indexed lookupSEC-10, LEG-24, PERF-31-2 wk9.3, 9.9, 9.10
6Fix RBAC CORR-4 + add (role × menu × action) test matrixCORR-41-2 wk9.4
7Frontend: Apollo cache-and-network + consolidate duplicate libsPERF-2, LEG-261-2 wk(Vol 3.2)
8Replace ldapjsldapts + fix cu-central-api httpRequest.jsLEG-8, SEC-7, CORR-91 wk9.10
9multer fileFilter + path traversal fixSEC (file)1 wk9.7
10Conicle SSRF allow-list + path normalizationSEC (SSRF)1 wk9.6, 9.7
11PII redaction ใน logger + ลบ console.log 77 ตัวLEG-24, SEC (log)1 wk9.9
12Commit centralize-api/yarn.lock + add engines.nodeLEG-15, LEG-180.5d9.10

รวม Wave 1 ≈ 3-4 เดือน — ปิด High ส่วนใหญ่


Wave 2 — Domain extraction → Go (Months 3-12)

Strategic rewrite ทีละ bounded context เริ่มจากที่ isolated ที่สุด

OrderBounded ContextWhy firstTarget
1document-renderer (PDF/Excel/qpdf)Self-contained + กำจัด root cause ของ SEC-1/PERF-1Go + chromedp + pdfcpu
2identity (auth, session, RBAC, device keys)ปลดล็อก context อื่น + แก้ SEC-3/4, CORR-4Go + Casbin + Postgres
3payroll (slip, tax)Read-mostly + clean boundaryGo + gqlgen, MSSQL adapter
4time-attendanceHigh-volume readGo + Redis cache
5leaveMulti-level workflowGo + saga
6welfare (WDHospital)ซับซ้อนที่สุด (13-state machine, 3,179 LOC controller)Go + state-machine package
7provident-fund, communication, organization, shared-kernelโมดูลที่เหลือGo

แต่ละ context ใช้ Hexagonal / DDD

cmd/                          # driving adapters (GraphQL via gqlgen, REST via chi)
internal/<context>/
  ├── domain/                 # entities, value objects, domain services
  ├── port/                   # use-case interfaces
  ├── adapter/                # db (sqlc), redis, sftp, pdf, smtp
  └── usecase/                # application services

Wave 3 — Frontend → Nuxt 3 (Months 6-15)

เริ่มหลัง identity + payroll Go services live (เพื่อให้ frontend ใหม่มี API เสถียร)

apps/web/                         # แทน vtrc-web
└── src/
    ├── app/                      # global shell, providers
    ├── processes/                # routing strategies (auth, onboarding)
    ├── pages/                    # route-level
    ├── widgets/                  # composite blocks
    ├── features/                 # user actions
    ├── entities/                 # domain slices
    │   └── <slice>/api/          # co-located gql operations
    └── shared/                   # ui-kit, api, lib, config, i18n

Modernization guardrails

#GuardrailPreventsChapter
G1No console.log ใน prod code — structured logger onlyLEG-249.9
G2No execSync / shell-out ใน request handlers — arg-array onlySEC-1, PERF-19.6
G3No secrets in source — secret-manager or KMSSEC-3, SEC-4, SEC-119.5
G4engines.node/go.mod enforced — CI fail on mismatchLEG-189.10
G5Lockfile committed — CI fail on driftLEG-159.10
G6Strict types (Go strict-default; TS strict: true)LEG-17(Vol 8)
G7Migrations forward-only — no ORM auto-sync ใน prodCORR-6(Vol 8.4)
G8One design system, one icon set, one DnD lib, one date libLEG-26(Vol 3)
G9No _bk/_copy files — .gitignore + pre-commitLEG-14
G10PII never in URLsSEC-89.6
G11CORS allow-list explicit — never *SEC-59.8
G12Rate limit ทุก public endpointSEC-109.10
G13Casbin policy test matrix สำหรับทุก (role × menu × action)CORR-49.4
G14Bounded contexts < 400 LOC/file — god-file fail CIPERF-6, LEG-21, LEG-23(Vol 8)
G15i18n ที่ presentation layer — never ใน error stringsLEG-30
G16No typosquat/git-URL deps — npm audit ใน CISEC-7, SEC-99.10
G17Refresh token verify (not decode) + rotate on useSEC (refresh)9.3

Cross-reference ไป volume อื่น

VolumeTopicRelevance
Volume 3.3Authentication & session internalsMechanics (Volume 10 = vulnerability)
Volume 8.4Schema management + syncModelToTableCORR-6 detail
Volume 8.9Identity across databasesMulti-DB auth mapping
Volume 8.10Indexing, transactions, performancePERF-3, PERF-4, CORR-2
Volume 8.11Data integrity risksPII, soft delete
Volume 15ModernizationGo (Hex/DDD) target

สิ่งที่ Volume นี้ไม่ได้ทำ

  • ไม่ได้ run npm audit / yarn audit — ต้องการ network install นอก scope static audit
  • ไม่ได้ audit TLS cipher suite ที่ edge nginx (config mount จาก host ไม่ได้อยู่ใน repo)
  • ไม่ได้ cover OWASP top 10 ทั้งหมด — เน้นเฉพาะที่พบจริงใน codebase
  • ไม่ได้ trace createslip/createtax end-to-end เพื่อยืนยันว่า qpdf RCE เป็น unauthenticated โดยแท้ — ต้อง follow-up

สรุป Volume 10

VTRC มี security posture ที่ 🔴 14 Critical + 12 High — ส่วนใหญ่มาจาก

  1. Secrets ใน repo (GCP key, prod device keys, .env, REACT_APP bundle) — Wave 0 แก้ได้เร็วที่สุด
  2. Injection (execSync × 5, /webdeployment, statLog SQLi) — Wave 0 + Wave 1
  3. Authentication bug (refresh decode, JWT 'secret' default, no rate limit) — Wave 0 + Wave 1
  4. Network exposure (CORS, phpMyAdmin, network_mode host) — Wave 0

Wave 0 (2-3 สัปดาห์) ลด Critical เหลือ 0 โดยไม่ต้อง refactor architecture ใหญ่ — เป็นจุดเริ่มที่คุ้มที่สุด

Wave 2-3 (ปฏิรูปเชิงโครงสร้าง) คือ modernization target ที่จะกำจัด root cause อย่างถาวร รายละเอียดอยู่ใน Volume 15