Skip to content

6 · Scorecard + known bugs (deep dive)

บทสรุปของ 01 ถึง 05 — severity catalog แบบเจาะลึกแต่ละ entry (exploit / impact / evidence / remediation) + health scorecard + modernization priority


Severity legend

Levelเกณฑ์Action
🔴 Criticalactive security/data-loss/availability riskต้องแก้ก่อน deploy ถัดไป
🟡 Highcorrectness/maintainability hazard, real impactวางแผนแก้ใน quarter นี้
🟢 Mediumfriction/hygiene, ยังไม่อันตรายระดับ activeแก้ตามโอกาส

Severity: Critical (🔴)

SEC-BENEFIT-01 · Production credential หลุดเข้า git

ที่:

  • benefit-api/.env (branch: prod):18,25,32,64 — MSSQL password ×3 (main/hrmi/vtrc) + SECRET_KEY
  • benefit-api/src/modules/exportPaymentReport/exportpaymentreport.service.ts:5271,5824,6151 — SFTP password hardcode

Evidence: .env มี NODE_ENV=prod และ IP จริงของ production database (10.188.128.61) commit อยู่ในไฟล์ — SFTP password ซ้ำ 3 จุดในโค้ดไม่ผ่าน env

Exploit:

bash
git clone <benefit-api-repo>
cat .env
# → เห็น DB_PASSWORD, DB_HRMI_PASSWORD, VTRC_DB_PASSWORD, SECRET_KEY
# → เชื่อมต่อ MSSQL ได้ทันทีถ้า network เปิดถึง 10.188.128.61

Impact: full production DB access (benefit + HRMI + vtrc schemas) + JWT forge ด้วย SECRET_KEY + SFTP access ไป FMIS

Remediation:

  1. Rotate credential ทั้งหมดทันที (DB ×3, JWT, SFTP)
  2. เพิ่ม .env เข้า .gitignore
  3. git filter-repo ล้าง history
  4. ย้ายไป secret manager (Vault / AWS Secrets Manager / k8s Secret)
  5. ย้าย SFTP password จาก hardcode ไป env var

SEC-BENEFIT-02 · Raw SQL string interpolation — SQL injection surface

ที่:

  • benefit-api/src/modules/payments/payments.service.ts:415-573, 640-720, 1032-1183, 1185-1435 (~700+ บรรทัด dynamic SQL)
  • benefit-api/src/modules/approveDisaster/approvedisaster.service.ts:415-417

Evidenceapprovedisaster.service.ts:415-417:

typescript
const approverData = await this.vtrcEntityManager.query(
  `SELECT * FROM MasterApprovers ma WHERE empCode = ${query.approverEmpCode} AND isActive = 1`,
);

empCode มาจาก HTTP query string ตรง ๆ โดยไม่มี quote ล้อม และไม่ใช้ parameterized query

Exploit:

bash
curl "https://benefit-api.../approveDisaster/list?approverEmpCode=1 OR 1=1--&isApprover=true"
# → query กลายเป็น: SELECT * FROM MasterApprovers ma WHERE empCode = 1 OR 1=1-- AND isActive = 1
# → คืนทุก row

Impact: SQL injection ที่ connection vtrc (schema เดียวกับ legacy vtrc-api) — อ่าน/เขียนข้อมูลพนักงานทั้งองค์กรได้ในทางทฤษฎี

Remediation:

  1. เปลี่ยนเป็น parameterized: entityManager.query('SELECT * FROM MasterApprovers WHERE empCode = @0 AND isActive = 1', [empCode])
  2. Sweep ทั้ง payments.service.ts (~700 บรรทัด) ด้วย pattern เดียวกัน
  3. Extract shared SQL builder function (ดู QUAL-BENEFIT-01) เพื่อลดจุด patch จาก 3 เหลือ 1

SEC-BENEFIT-03 · Endpoint เบิกค่ารักษาพยาบาลเป็น @Public() ทั้งชุด

ที่: benefit-api/src/modules/wdhospitals/wdhospitals.controller.ts:24-40

Evidence:

typescript
@Public()
@Post('/add')
insertWdHospitals(@Body() body: RequestWdHospitalsDto) { ... }

@Public()
@Post('/update/:status')
updateWdHospitals(...) { ... }

@Public()
@Get('/detail/:id')
getWdHospitalsById(...) { ... }

Exploit:

bash
# สร้างรายการเบิกโดยไม่ login
curl -X POST https://benefit-api.../wdHospitals/add \
  -H "Content-Type: application/json" \
  -d '{"empCode":"E001","hospitalName":"...","amount":50000,...}'

# อ่าน detail (มี identity card, บัญชีธนาคาร)
curl https://benefit-api.../wdHospitals/detail/<uuid>

Impact: ใครก็สร้าง/แก้ไข/อ่านรายการเบิกค่ารักษาพยาบาล (มี PII) ได้โดยไม่ login — ต่างจาก disaster module ที่ endpoint คล้ายกันมี auth ปกติ — บ่งชี้ว่าอาจเปิดไว้ตอน debug แล้วลืมปิด

Remediation: ลบ @Public() ทั้ง 3 endpoint — ตรวจสอบกับทีม frontend ก่อนว่าทำไมตั้งใจเปิด หรือเป็นความผิดพลาด


SEC-BENEFIT-04 · Batch job endpoints เป็น @Public()

ที่: benefit-api/src/modules/batchJob/manualBatch.controller.ts:13-34

Evidence: @Public() บน importFuneral, importInheritance, updateFuneral, updateInheritance

Impact: ใครก็ trigger batch import/update ไป HRMI ได้โดยไม่ auth — อาจทำให้ข้อมูล HRMI เสียหาย

Remediation: ลบ @Public() + เพิ่ม role check (เฉพาะ admin/batch operator)


SEC-BENEFIT-05 · Upload endpoints เป็น @Public()

ที่: benefit-api/src/modules/uploadFiles/upload.controller.ts:13-22

Impact: อัปโหลด/ดาวน์โหลดไฟล์แนบโดยไม่ auth — อาจใช้เป็น storage สำหรับ malware หรือ leak เอกสาร confidential

Remediation: ลบ @Public() + เพิ่ม file type/size validation


Severity: High (🟡)

CORR-BENEFIT-01 · ข้อมูล WdHospitals ซ้ำ 2 schema ไม่มี sync

ที่:

  • benefit-api/src/entities/wdHospitals.entity.tsbenefit.dbo.wdHospitals
  • raw SQL ใน payments.service.ts:493-573vtrc.dbo.WDHospitals

Impact: data drift — flow บางตัวเขียนฝั่งเดียวแล้วลืมอีกฝั่ง ทำให้ list ฝั่ง benefit กับ list ฝั่ง vtrc-api ไม่ตรงกัน

Remediation: ตัดสินใจว่าตารางไหนคือ source of truth แล้ว migrate อีกตารางให้เป็น read-only mirror + เพิ่ม reconcile job


QUAL-BENEFIT-01 · Logic ซ้ำกันหลายร้อยบรรทัดใน payments.service.ts

ที่: payments.service.ts:488-573 vs 640-720 vs 1341-1427 — SQL string-building 40+ field copy-paste ซ้ำ 3 รอบ

Impact: ถ้าจะ fix SEC-BENEFIT-02 ต้องแก้ 3 ที่พร้อมกัน — miss ที่เดียว = injection ยังอยู่

Remediation: extract เป็น shared function เดียว


SEC-BENEFIT-06 · /ping-centrail SSRF risk

ที่: benefit-api/src/app.controller.ts:13-16

Evidence: @Public() + this.http.pingCheck('nestjs-docs', query.url) — รับ URL จาก query string แล้ว ping

Impact: Server-Side Request Forgery — attacker ให้ server ping internal IP (http://10.188.128.61:1433) เพื่อ scan network

Remediation: ลบ endpoint หรือ restrict URL allowlist


SEC-BENEFIT-07 · /testProfile debug endpoint ค้างใน prod

ที่: benefit-api/src/app.controller.ts:19-22

Impact: debug endpoint ที่ decode JWT แล้วคืน profile — เปิดใน production

Remediation: ลบหรือ gate ด้วย NODE_ENV !== 'prod'


OBS-BENEFIT-01 · console.log PII ปนใน production stdout

ที่: กระจายทั่วทุก service (payments.service.ts:82,85,90,145,265..., approvedisaster.service.ts:418)

Evidenceapprovedisaster.service.ts:418:

typescript
console.log('approverData eiei', approverData);

Impact: PII (identity card, บัญชีธนาคาร, empCode) หลุดไป log aggregator — PDPA violation

Remediation: ลบ console.log ทั้งหมด + ใช้ structured logger ที่ redact field sensitive


Severity: Medium (🟢)

QUAL-BENEFIT-02 · validateData() เป็น stub ที่ return true เสมอ

ที่: benefit-api/src/modules/disaster/disaster.service.ts:361-366

Impact: method ชื่อสื่อว่าตรวจสอบแต่ไม่ตรวจอะไรจริง — เรียกใช้ทั้งใน create และ update

Remediation: implement validation จริง หรือลบ method ออก


QUAL-BENEFIT-03 · docType เป็น magic number ไม่มี enum กลาง

ที่: กระจายทั่ว payments.service.ts, approvedisaster.service.ts, disbursement.service.ts — เลข 1-5 hardcode

Remediation: สร้าง TypeScript enum DocType { MEDICAL=1, DISASTER=2, ... }


LEG-BENEFIT-01 · sftp-upload + ssh2-sftp-client สอง package

ที่: benefit-api/package.json:57-58 — import จริงใช้ ssh2-sftp-client เท่านั้น

Remediation: ลบ sftp-upload ออกจาก dependencies


QUAL-BENEFIT-04 · uploads/ มีไฟล์ผู้ใช้จริง commit ใน git

ที่: benefit-api/uploads/disaster/2023-02-27/*.png

Impact: ไฟล์ภาพประกอบการเบิกภัยพิบัติ (อาจมี PII) ติด git history ตั้งแต่ปี 2023

Remediation: เพิ่มเข้า .gitignore + ลบออกจาก history


QUAL-BENEFIT-05 · Base entity สองตัวทำหน้าที่ซ้ำ

ที่: entities/base.ts vs baseBenefit.entity.ts — field เกือบเหมือนกันยกเว้น isDeleted และ default createdByEmpcode

Remediation: รวมเป็น base entity เดียว


OBS-BENEFIT-02 · Cron ไม่มี distributed lock

ที่: src/crontab/ — 2 job ที่รันด้วย @Cron

Impact: ถ้า deploy >1 replica → cron รันซ้ำพร้อมกัน

Remediation: เพิ่ม Redis lock หรือใช้ APP_NO-style leader election


Health Scorecard

Dimensionสถานะเหตุผล
Auth pipeline🔴endpoint เบิกเงินสำคัญเป็น @Public() + ไม่มี RBAC + batch/upload public
Persistence & transaction🔴raw SQL cross-schema ซ้ำ 3-4 จุด + ข้อมูลซ้ำ 2 schema ไม่มี reconcile
Secrets management🔴.env production credential + SFTP password hardcode commit ใน git
External integration resilience🔴ไม่มี retry/timeout/circuit breaker ต่อ centralize server และ SFTP
Code duplication🔴SQL builder logic 40+ field copy-paste ซ้ำ 3 รอบ
Background jobs🟡ไม่มี distributed lock, ไม่มี retry/dead-letter, แต่ scope งานเล็ก (2 job)
Logging & observability🔴console.log PII ปนใน production stdout ทั่ว repo
Layering (controller/service/entity)🟡แยกชัดเป็น NestJS module มาตรฐาน แต่ service บางตัวยาวเกิน 5,000 บรรทัด
Test coverage🟡มี .spec.ts ในบางโมดูล (dropdown, disaster, export-payments, upload) แต่ไม่ครบ
Config & secrets🔴ซ้ำกับ secrets management ด้านบน

คะแนนรวม: 0 🟢 / 3 🟡 / 7 🔴


Public contract — สิ่งที่ต้อง maintain

รายการที่ตั้งเหตุผลที่ต้องคง
docType magic numbers 1-5กระจายทั่ว payments/disaster/disbursementfrontend ส่งเลขเหล่านี้ — เปลี่ยนต้อง sync ทั้งคู่
Triple DB connection names (db, dbHRMI, vtrcDB)configuration.tsinjection token ที่ service inject — rename กระทบทุก service
SECRET_KEY env var nameconfiguration.ts:5deployment pipeline ใช้อยู่
wdHospitals vs WDHospitals casingentity vs raw SQLคนละ schema คนละ casing — ห้าม unify โดยไม่ migrate
SFTP paths (upload/fmis/bgdoc/ ฯลฯ)exportpaymentreport.service.tsFMIS server คาดหวัง path เหล่านี้
@Public() บน dropdowndropdown.controller.tsfrontend อาจเรียกตอนยังไม่ login — ลบต้อง verify กับ frontend ก่อน

Modernization priority (ROI-weighted)

  1. Rotate credential ทั้งหมดที่หลุดใน .env และ SFTP password — ทำก่อนอย่างอื่น เพราะเป็น production credential ที่ active
  2. แก้ raw SQL ให้ใช้ parameterized query — ปิด SEC-BENEFIT-02 โดยไม่ต้องเปลี่ยน business logic
  3. Extract shared function สำหรับ WDHospitals SQL builder — ลด LOC + ลดจุด patch จาก 3 เหลือ 1
  4. ลบ @Public() จาก wdHospitals / batchJob / upload — ปิด SEC-BENEFIT-03/04/05 (verify กับ frontend ก่อน)
  5. ย้าย secret ไป secret manager — ต่อเนื่องจากข้อ 1
  6. ลบ /ping-centrail และ /testProfile — ปิด SSRF + debug leak
  7. เพิ่ม distributed lock ให้ cron ถ้า deploy >1 replica
  8. Reconcile benefit.wdHospitals กับ vtrc.WDHospitals — ตัดสินใจ source of truth

การเปลี่ยนแปลงจาก shallow V2

บทนี้ขยายจาก 113 บรรทัด (ตารางสรุป) เป็น deep dive แบบ V1:

  • แต่ละ Critical/High entry มี Exploit / Impact / Evidence / Remediation ครบ
  • เพิ่ม SEC-BENEFIT-04 (batch public), SEC-BENEFIT-05 (upload public), SEC-BENEFIT-06 (SSRF), SEC-BENEFIT-07 (debug endpoint) ที่ shallow V2 ไม่ระบุแยก
  • เพิ่ม Public contract list

จบ benefit-api

กลับไป index หรือดู Volume 6 overview