6 · Scorecard + known bugs (deep dive)
บทสรุปของ 01 ถึง 05 — severity catalog แบบเจาะลึกแต่ละ entry (exploit / impact / evidence / remediation) + health scorecard + modernization priority
Severity legend
| Level | เกณฑ์ | Action |
|---|---|---|
| 🔴 Critical | active security/data-loss/availability risk | ต้องแก้ก่อน deploy ถัดไป |
| 🟡 High | correctness/maintainability hazard, real impact | วางแผนแก้ใน quarter นี้ |
| 🟢 Medium | friction/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_KEYbenefit-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:
git clone <benefit-api-repo>
cat .env
# → เห็น DB_PASSWORD, DB_HRMI_PASSWORD, VTRC_DB_PASSWORD, SECRET_KEY
# → เชื่อมต่อ MSSQL ได้ทันทีถ้า network เปิดถึง 10.188.128.61Impact: full production DB access (benefit + HRMI + vtrc schemas) + JWT forge ด้วย SECRET_KEY + SFTP access ไป FMIS
Remediation:
- Rotate credential ทั้งหมดทันที (DB ×3, JWT, SFTP)
- เพิ่ม
.envเข้า.gitignore git filter-repoล้าง history- ย้ายไป secret manager (Vault / AWS Secrets Manager / k8s Secret)
- ย้าย 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
Evidence — approvedisaster.service.ts:415-417:
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:
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
# → คืนทุก rowImpact: SQL injection ที่ connection vtrc (schema เดียวกับ legacy vtrc-api) — อ่าน/เขียนข้อมูลพนักงานทั้งองค์กรได้ในทางทฤษฎี
Remediation:
- เปลี่ยนเป็น parameterized:
entityManager.query('SELECT * FROM MasterApprovers WHERE empCode = @0 AND isActive = 1', [empCode]) - Sweep ทั้ง
payments.service.ts(~700 บรรทัด) ด้วย pattern เดียวกัน - 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:
@Public()
@Post('/add')
insertWdHospitals(@Body() body: RequestWdHospitalsDto) { ... }
@Public()
@Post('/update/:status')
updateWdHospitals(...) { ... }
@Public()
@Get('/detail/:id')
getWdHospitalsById(...) { ... }Exploit:
# สร้างรายการเบิกโดยไม่ 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.ts→benefit.dbo.wdHospitals- raw SQL ใน
payments.service.ts:493-573→vtrc.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)
Evidence — approvedisaster.service.ts:418:
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/disbursement | frontend ส่งเลขเหล่านี้ — เปลี่ยนต้อง sync ทั้งคู่ |
Triple DB connection names (db, dbHRMI, vtrcDB) | configuration.ts | injection token ที่ service inject — rename กระทบทุก service |
SECRET_KEY env var name | configuration.ts:5 | deployment pipeline ใช้อยู่ |
wdHospitals vs WDHospitals casing | entity vs raw SQL | คนละ schema คนละ casing — ห้าม unify โดยไม่ migrate |
SFTP paths (upload/fmis/bgdoc/ ฯลฯ) | exportpaymentreport.service.ts | FMIS server คาดหวัง path เหล่านี้ |
@Public() บน dropdown | dropdown.controller.ts | frontend อาจเรียกตอนยังไม่ login — ลบต้อง verify กับ frontend ก่อน |
Modernization priority (ROI-weighted)
- Rotate credential ทั้งหมดที่หลุดใน
.envและ SFTP password — ทำก่อนอย่างอื่น เพราะเป็น production credential ที่ active - แก้ raw SQL ให้ใช้ parameterized query — ปิด SEC-BENEFIT-02 โดยไม่ต้องเปลี่ยน business logic
- Extract shared function สำหรับ WDHospitals SQL builder — ลด LOC + ลดจุด patch จาก 3 เหลือ 1
- ลบ
@Public()จาก wdHospitals / batchJob / upload — ปิด SEC-BENEFIT-03/04/05 (verify กับ frontend ก่อน) - ย้าย secret ไป secret manager — ต่อเนื่องจากข้อ 1
- ลบ
/ping-centrailและ/testProfile— ปิด SSRF + debug leak - เพิ่ม distributed lock ให้ cron ถ้า deploy >1 replica
- 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 →