Skip to content

19.1 · 90-day emergency security fixes

ขอบฟ้า วัน 1–90 — ปิดช่องโหว่ที่ active จาก deep scorecards และ Vol 12.5 backdoor / secrets / SQLi clusters โดยจัดเป็นสัปดาห์ที่ประสานข้าม repo ได้

/ Days 1–90 — close active holes from deep scorecards and the Vol 12.5 clusters, sequenced so shared secrets and backdoors can be closed in coordinated passes.

เป้าหมายของขอบฟ้านี้ไม่ใช่ "rewrite architecture" แต่คือ หยุดเลือด: credential ที่หลุดใช้ไม่ได้, backdoor หาย, injection พื้นผิวหลักปิด, endpoint ที่คืนเงินเดือน/เบิกค่ารักษาโดยไม่ login ถูกบังคับ auth


สัปดาห์ 0 — เตรียม coordination (ก่อน deploy แก้ใด ๆ)

Checklist ก่อนเริ่ม

ขั้นงานทำไม
0.1นัดเจ้าของ repo ของ secrets cluster ทั้งชุดshared SECRET_KEY / sa ต้อง rotate พร้อมกัน
0.2สำรอง access log / deploy manifest ของ UAT+prodใช้ตรวจ traffic ผิดปกติหลังปิด @Public()
0.3เปิด channel ร่วม + freeze feature ที่แตะ authลด merge conflict ตอนลบ backdoor
0.4อ่าน 19.4 What NOT to rewriteห้าม "แก้ typo ไปด้วย" ใน PR security

Secrets ที่ต้อง rotate พร้อมกัน (ชุด A — Domain JWT)

จาก Vol 12.5 secrets cluster และ pms-api SEC-PMS-02:

RepoFindingสิ่งที่หลุด
benefit-apiSEC-BENEFIT-01MSSQL ×3 + SECRET_KEY + SFTP password hardcode — benefit-api/06
recruitment-apiSEC-RECRUIT-01MSSQL + JWT + SMTP + reCAPTCHA + GCP SA
ai-recruitment-apiSEC-AIRECRUIT-01OpenAI/remove.bg + DB
pms-apiSEC-PMS-02UAT .env + shared SECRET_KEY + GCP SA — pms-api/06

กฎ: ค่า SECRET_KEY เดียวกัน → rotate ทุก service ในชุด A ในนาทีเดียวกัน แล้ว redeploy พร้อมกัน — มิฉะนั้น JWT ที่ forge จาก secret เก่ายัง valid กับ service ที่ยังไม่ rotate

Secrets ชุด B — MSSQL sa / shared ops

RepoFindingหมายเหตุ
vtrc-commonSEC-COMMON-03.env + Docker bake
job-scheduler-apiSEC-JOBSCHED-01.env with sa password — share กับชุด B
2way-vtrc-apiSEC-2WAYVTRC-03Mongo + MSSQL sa in source
chat-center-apiSEC-CHAT-04prod/UAT .env
meeting-vtrc-apiSEC-MTVTRC-01.env* plaintext
vtrc-rc-backofficeSEC-VTRC-RC-01Stimulsoft conn + AES key — vtrc-rc-backoffice/07 ROI #1

สัปดาห์ 1 — Day-one kills (effort ชั่วโมง, impact Critical)

งานเหล่านี้ตรงกับ ROI อันดับต้นของหลาย scorecard และ quick-win เดิม — ทำวันแรกได้โดยไม่รอ secret manager

1.1 ลบ backdoor login / bypass token

IDRepoEvidence (deep)Remediation ขั้นต่ำ
SEC-2WAYAPI-012way-apiauth.service.ts (branch: prod):184-240 — hardcode i3admin → JWT admin 48hลบทั้ง if block; อย่า comment ไว้ — 2way-api/04
SEC-2WAYVTRC-012way-vtrc-apibypass token "testnaja" ใน middlewareลบ string check
SEC-CU-04cu-central-api/auth/signinbypass passwordlessลบ endpoint
SEC-MTVTRC-05meeting-vtrc-apiglobal /meeting/secure/* auth commenteduncomment / restore guard

หลังลบ SEC-2WAYAPI-01: rotate SECRET_JWT_TWOWAY ทันที — token ที่ออกจาก backdoor ยังใช้ได้จนหมดอายุ 48h (2way-api/04 Remediation)

ตัวอย่าง verification:

bash
# ต้องล้มหลัง patch — อย่าใส่รหัสจริงใน ticket; ใช้ค่าจาก scorecard เฉพาะใน controlled test
curl -X POST "https://<host>/2way-api/api/v1/auth/" \
  -H "Content-Type: application/json" \
  -d '{"username":"i3admin","password":"<known-backdoor>"}'
# expect: ไม่ได้ JWT admin / ไม่ได้ mockProfile.level=1

1.2 ถอด @Public() ที่คืน PII / เงินเดือน / write ทางการเงิน

IDRepoEndpoint / surfaceROI source
SEC-PMS-01pms-apiGET /budget/getEmployeeSalarybudget.controller.ts (branch: uat):49-53ROI #1 — pms-api/06
SEC-BENEFIT-03benefit-api@Public() wdHospitals CRUD — wdhospitals.controller.ts:24-40ROI #4 — benefit-api/06
SEC-BENEFIT-04 / -05benefit-apibatchJob / upload @Public()ปิดคู่กับ -03
SEC-RECRUIT-03recruitment-api@Public() import_blacklistVol 12.5 backdoor cluster
SEC-2WAYLINE-012way-line-serviceทุก endpoint @Public()ถอด decorator ในไฟล์เดียว
CRIT-03centralize-api@Public() ทุก routeauth machinery dead — Vol 12.5

ข้อควรระวัง: dropdown ของ benefit ที่เป็น @Public() อาจถูก frontend เรียกก่อน login — อย่าถอดโดยไม่ verify (ดู benefit-api Public contract และ 19.4)

Regression ขั้นต่ำหลัง SEC-PMS-01 (pms-api/06 checklist):

  1. GET /budget/getEmployeeSalary โดยไม่มี token → 401
  2. Login ปกติด้วย JWT + apiKey ยังผ่าน checkRoleRc ได้

1.3 Fail-fast JWT / secret defaults

IDRepoการแก้
SEC-VTRC-API-02vtrc-apiลบ default JWT_SECRET = 'secret' — throw ตอน boot — vtrc-api/10 ROI #3
SEC-CU-01cu-central-apiJWT_SECRET default empty → fail-fast
SEC-2WAYAPI-022way-apiลบ AES/SSO fallback ในซอร์ส — 2way-api/04 ROI #2

Pattern ที่แนะนำจาก 2way-api/04 remediation sample:

typescript
const secret = process.env.SSO_AES_SECRET_KEY;
if (!secret) {
  throw new Error('SSO_AES_SECRET_KEY is required');
}

1.4 Frontend day-one (XSS / secret hygiene)

IDRepoงานROI
SEC-VTRC-WEB-02vtrc-webwrap dangerouslySetInnerHTML ด้วย dompurifyROI #1 — vtrc-web/07
SEC-VTRC-WEB-03vtrc-webCSP headerROI #2
SEC-VTRC-RC-01vtrc-rc-backofficeลบ .env จาก tracking + rotateROI #1 — vtrc-rc-backoffice/07
SEC-VTRC-RC-04vtrc-rc-backofficedompurifyROI #3
SEC-VTRC-RC-06vtrc-rc-backofficeCSP headerROI #4
LEG-CU-01cu-central-apiแก้ typosquat axoisaxiossupply-chain, effort S

สัปดาห์ 2–3 — Rotate secrets + scrub git history

ลำดับการทำ (อย่าสลับ)

  1. Deploy โค้ดที่อ่าน secret จาก env ใหม่ (ยังไม่ rotate) — ยืนยัน boot ไม่ fallback
  2. Rotate ค่าจริงใน secret store / env ของ cluster ชุด A พร้อมกัน → redeploy ชุด A
  3. Rotate ชุด B (sa / Stimulsoft / Mongo) พร้อมกัน
  4. Invalidate session — logout บังคับหรือรอ TTL สั้นสุดที่มี
  5. git filter-repo / BFG ลบ .env, *-sa-key.json จาก history ทุก repo ใน cluster
  6. Force-push policy + แจ้งทุก clone ให้ re-clone
  7. ติดตั้ง secret scanning (gitleaks / GitHub secret scanning) เป็น required check — กันรั่วซ้ำ

อ้าง remediation ของ benefit-api SEC-BENEFIT-01 และ pms-api SEC-PMS-02

สิ่งที่ต้อง rotate นอก JWT

ประเภทตัวอย่างที่พบFinding
MSSQL passwordsbenefit ×3 schemas, pms ×4, recruitmentsecrets cluster
SFTP / FMIShardcode ใน exportpaymentreport.service.ts:5271,5824,6151SEC-BENEFIT-01
SMTPpms / recruitmentSEC-PMS-02, SEC-RECRUIT-01
GCP SA JSONpms-api/vtrc-gcp-sa-key.json, recruitmentอย่า paste ใน chat/ticket
Device API keyshardcode ใน configuration.ts (pms)ย้าย env + rotate
AES / Stimulsoftrc-backoffice .envSEC-VTRC-RC-01

Effort โดยประมาณจาก pms-api ROI #3: 1–3 วัน (ประสานหลายทีม) — ไม่ใช่ "แก้ไฟล์เดียว"


สัปดาห์ 3–5 — SQL injection cluster

จาก Vol 12.5 SQLi cluster และ ROI ของ benefit / 2way:

IDRepoEvidence สั้นEffort
SEC-BENEFIT-02benefit-apipayments.service.ts ~700+ บรรทัด dynamic SQL; approvedisaster.service.ts:415-417empCode = ${query.approverEmpCode}L — ROI #2–3
SEC-2WAYAPI-032way-apiannounces / surveys / complaints string concatM — ROI #4
SEC-RECRUIT-02recruitment-apidynamic EXEC AI compareM
SEC-CHAT-01chat-center-apirooms/tags query stringM
SEC-VTRC-API-07vtrc-apitemplate literal raw SQLS–M
SEC-COMMON-02vtrc-commonaddress provCode/amphCodeS

ลำดับ execution ที่แนะนำ: แก้จุดเล็ก (SEC-COMMON-02, SEC-VTRC-API-07) ก่อนเพื่อได้ momentum แล้วแยก sprint สำหรับ SEC-BENEFIT-02 พร้อม extract shared WDHospitals SQL builder (benefit-api ROI #3)

Parameterized pattern จาก 2way-api/04:

typescript
const rows = await this.entityManager.query(
  `SELECT id, topic_name FROM t_content
   WHERE emp_code = @0 AND offer_date >= @1 AND offer_date <= @2`,
  [empCode, fromDate, toDate],
);

และจาก benefit-api SEC-BENEFIT-02:

typescript
await this.vtrcEntityManager.query(
  'SELECT * FROM MasterApprovers WHERE empCode = @0 AND isActive = 1',
  [empCode],
);

สัปดาห์ 4–6 — Command injection + unauthenticated routes (vtrc-api)

ตรง vtrc-api/10 ROI #1 และ #4:

IDที่การแก้
SEC-VTRC-API-01routes.js:73,120,227,277,355execSync(qpdf --encrypt ${passwordPDF}...)เปลี่ยนเป็น execFileSync('qpdf', [...args]) หรือ worker; validate password alphanumeric
SEC-VTRC-API-03/webdeployment shell binaryเพิ่ม @auth / secret header หรือปิดใน prod
SEC-VTRC-API-04/file/:id/download ไม่มี apiKeycopy middleware จาก route ข้างเคียง
SEC-VTRC-API-06CORS typo corssแก้ key name พร้อม CORS allowlist fix — ดู 19.4 เพราะ corss เป็น public-ish config key

SEC-VTRC-API-01 เป็น ROI #1 ของ vtrc-api เพราะปิดทั้ง security และ performance (event-loop block จาก execSync) ในครั้งเดียว


สัปดาห์ 5–8 — Auth design ที่พัง (ไม่ใช่แค่ bypass)

จาก Vol 12.5 broken auth design:

IDRepoปัญหางานใน 90 วัน
CORR-SSO-01sso-apiJWT sign() commented + JwtService not injectedredesign หรือลบ guard ที่ทำให้ 2FA พัง — เริ่ม design ในสัปดาห์ 5; ship ขั้นต่ำในสัปดาห์ 8
SEC-2WAYAPI-052way-apiignoreExpiration: trueตั้ง false — ROI #5
SEC-WORKFLOW-01workflow-apiไม่ verify JWT; role จาก sourceDBเริ่ม spike — อาจล้นไป 6-month ถ้า L
SEC-AIRECRUIT-02ai-recruitment-apisignature only — no roleเพิ่ม role check
SEC-JOBSCHED-02job-scheduler-apicron POST ไม่มี authใส่ service secret / internal-only
SEC-2WAYAPI-042way-api@Public() บน complaints / LINE / announce mutationsคุย frontend/LINE ก่อนตัด — ROI #3

สัปดาห์ 6–10 — Token-in-URL mitigation (ขั้นฉุกเฉินก่อน rewrite เต็ม)

Deep findings: SEC-VTRC-RC-02 (/pathLogin/:token/:refreshToken), SEC-BONEW-01Vol 12.5 frontend cluster

HttpOnly cookie เต็มรูปแบบเป็น investment ใหญ่ (vtrc-web ROI #6, vtrc-rc-backoffice ROI #7) — อย่าพยายามทำครบใน 90 วัน

งานฉุกเฉินที่ทำได้ก่อน:

  1. หยุด access-log ที่ log path/query ที่มี token (nginx / gateway)
  2. ย้าย SSO handoff จาก path segment ไป POST body (vtrc-rc-backoffice ROI #2)
  3. ลด TTL ของ token ที่ยังต้องอยู่ใน URL ชั่วคราว
  4. Audit referrer / analytics ที่อาจเก็บ URL

งานเต็ม (HttpOnly + CSRF) ไปอยู่ 19.3


สัปดาห์ 8–12 — Mobile RSA + infra hardening (เริ่มได้ / อาจล้น)

IDRepoหมายเหตุ
SEC-MOBILE-01 / SEC-NEW-01vtrc-mobile / new-vtrc-mobileRSA private key hardcode — ต้องคู่กับ app store release
SEC-CU-06cu-central-apiTLS / encrypt hardening
SEC-2WAYLINE-022way-line-servicehardcoded IP ไม่มี TLS จาก 2way-api
SEC-2WAYAPI-06 / -072way-apiDB encrypt + LINE URL จาก env + HTTPS — ROI #7–8

ถ้า app-store cycle ไม่ทัน 90 วัน — บันทึกเป็น spillover ไปเดือน 4 พร้อม interim network ACL


Definition of Done — 90 วัน

ถือว่าขอบฟ้า 90 วันสำเร็จเมื่อ:

#เกณฑ์หลักฐาน
1Backdoor cluster หายจาก canonical branch ที่ deployPR merged + curl regression ล้ม
2Secrets ชุด A+B ถูก rotate และไม่เหลือใน working treesecret scan clean + history scrubbed
3@Public() บน salary / wdHospitals write / blacklist import ปิด401 โดยไม่มี token
4SQLi จุด Critical ใน cluster มี parameterized หรือถูก feature-flag ปิดcode review + smoke test
5execSync qpdf ไม่รับ shell string อีกexecFileSync หรือ equivalent
6ไม่มี PR ที่ "แก้ typo public contract" ปนมาchecklist 19.4

สิ่งที่ตั้งใจเลื่อนออกนอก 90 วัน

  • NestJS consolidation / แยก REST PDF worker — 19.3
  • Central secret manager เต็มรูปแบบ (หลัง rotate มือเสร็จ)
  • Sequelize 5→6, Node 12→20, Apollo v2→v4 — vtrc-api/10 ระบุว่าไม่คุ้มถ้าไม่มี feature ใหม่บังคับ
  • รวม vtrc-backoffice + vtrc-backoffice-new
  • แก้ CORR-VTRC-API-01 transaction arg ทั้งระบบ — ไป 19.2

ไป 19.2 · 6-month correctness