Skip to content

6.8 · Security debt register

บทนี้สรุป security debt ที่พบใน benefit-api ทั้งหมด จัดเรียงตาม severity พร้อม code reference, impact, และ remediation


Severity rubric

Severityนิยาม
Criticalexploit ได้ทันที, รับผลกระทบ wide scope (data leak / RCE)
Highexploit ได้ง่าย, ผลกระทบ medium-high scope
Mediumexploit ต้องมี precondition, ผลกระทบจำกัด
Lowdefense in depth, hardening

S1 — SECRET_KEY เป็น SSH public key และ commit เข้า git

Severity: Critical

Files:

  • .env:64 — value
  • libs/jwt/jwt.strategy.ts:14 — ใช้เป็น JWT secret
  • config/configuration.ts:9 — mapping

Impact:

ค่า SECRET_KEY คือ SSH RSA public key (prefix AAAAB3NzaC1yc2EAAAADAQABAAABAQ) ที่ commit เข้า git → ใครที่เคยเข้าถึง repo สามารถ forge JWT ที่ benefit-api ยอมรับได้ ทำให้ระบบ auth ทั้งหมด bypass ได้

Remediation:

  1. Generate JWT secret ใหม่ (HMAC key 256-bit สุ่ม)
  2. หมุน secret ทุก environment (dev/uat/prod)
  3. ลบ secret ออกจาก git history (git filter-branch หรือ BFG)
  4. ย้าย secret ไปยัง secret manager (Vault, AWS Secrets Manager, GCP Secret Manager)
  5. เพิ่ม pre-commit hook ป้องกัน secret leak

S2 — benefit-api เขียนตรงไปยัง vtrc schema (cross-context write)

Severity: High (architecture)

Files:

  • app.module.ts:30-50vtrc TypeORM connection
  • payments.service.ts:568UPDATE WDHospitals
  • export-payments.service.ts:591UPDATE WDHospitals
  • wdHospitals.entity.ts:3@Entity('WDHospitals', { schema: 'vtrc' })

Impact:

benefit-api และ vtrc-api เป็น writer คู่กันไปยังตารางเดียวกัน (vtrc.WDHospitals) → no single source of truth, race condition, การแก้ไข schema ในอนาคตต้อง coordinate หลายฝั่ง

Remediation:

  1. benefit-api ควรเรียก vtrc-api ผ่าน GraphQL/REST ไม่ใช่เขียน DB ตรง
  2. ระหว่างทาง ตั้ง ownership matrix ชัดเจนว่าตารางใดเป็นของ service ใด
  3. เพิ่ม DB-level audit trigger ที่ WDHospitals เพื่อ track writer

ดู Volume 16 · Modernization Roadmap สำหรับกลยุทธ์ strangle


S3 — SFTP password hardcoded ใน source

Severity: Critical

Files:

  • exportpaymentreport.service.ts:135-145password: '~x-:#"nTb-2k+_TaE'

Impact:

Credential SFTP (ftpfmis account) อยู่ใน git และ baked เข้า Docker image → ใครได้ source คือได้สิทธิ์ write/read file บน FMIS gateway

Remediation:

  1. หมุน SFTP password ทันที
  2. ย้ายไปยัง environment variable (process.env.SFTP_PASSWORD)
  3. Production: เก็บใน secret manager
  4. Consider key-based auth แทน password

S4 — .env commit เข้า git และ baked เข้า Docker image

Severity: Critical

Files:

  • .env (root of repo) — committed
  • Dockerfile:25ADD . . copies .env into image
  • .gitignore.env ไม่ได้ถูก ignore

Impact:

Production credentials (MSSQL sa password h@ckm3SA?, SFTP password, JWT secret) รั่วออกไปทั้งหมดทุก environment ที่รัน image

Remediation:

  1. Add .env to .gitignore
  2. Remove .env from git history
  3. Remove ADD . หรือเพิ่ม .dockerignore ที่ exclude .env
  4. หมุนทุก credential ที่เคยอยู่ใน .env
  5. Use Docker secrets / K8s secrets / env injection at runtime

S5 — SQL injection (20+ sites)

Severity: Critical (aggregate)

Files: ดู 6.4 — Critical: 2 sites, High: 18+ sites

Impact:

เนื่องจากทุก connection ล็อกอินด้วย sa (S11) SQL injection จุดเดียว = full DB compromise: read PII, execute xp_cmdshell, lateral movement

Remediation:

  1. แปลง raw query ทั้งหมดเป็น parameterized (? placeholder หรือ QueryBuilder)
  2. เพิ่ม ESLint rule ตรวจ entityManager.query(\...`)` pattern
  3. Code review checklist: ห้าม template literal ใน raw query
  4. สั่ง pentest หลังแก้

S6 — @Public() บน mutation endpoints

Severity: Critical

Files:

  • wdhospitals.controller.ts:14,22@Public() @Post('add'), @Post('update')
  • upload.controller.ts:8@Public() @Post('upload')
  • manualBatch.controller.ts:10,16@Public() @Get('pull'), @Get('push')
  • app.controller.ts:18@Public() @Get('ping-centrail')

Impact:

Mutation endpoints (add/update WDHospitals, trigger cron, upload files) bypass auth ทั้งหมด → anonymous attacker สร้าง/แก้ไข transaction ได้

Remediation:

  1. ลบ @Public() จาก mutation endpoints ทั้งหมด
  2. เพิ่ม role check ใน controller (@Roles('admin') หรือ custom decorator)
  3. Audit log ทุก mutation

S7 — SSRF ผ่าน /ping-centrail

Severity: High

Files:

  • app.controller.ts:18-25@Public() @Get('ping-centrail')
18:25:benefit-api/src/app.controller.ts
  @Public()
  @Get('ping-centrail')
  async pingCentrail(@Query('url') url: string) {
    const response = await axios.get(url);
    return response.data;
  }

Impact:

Endpoint รับ URL ตรงจาก query string แล้ว fetch ทันที → attacker สามารถ:

  • Probe internal network (http://10.x.x.x/admin)
  • อ่าน metadata service ของ cloud (http://169.254.169.254/...)
  • ทำ DoS ผ่าน external request

Remediation:

  1. ลบ endpoint นี้ทันที (debug utility — ไม่ควรอยู่ใน prod)
  2. ถ้าจำเป็น ใช้ allowlist ของ domains ที่อนุญาต

S8 — File upload ไม่จำกัด type

Severity: High

Files:

  • upload.controller.ts:8@Public() @Post('upload')

Impact:

ใครก็ upload ไฟล์เข้า /uploads/ ได้โดยไม่ login — ใช้เป็น staging ground สำหรับ malware, หรือเก็บ illegal content บน infrastructure ของสภากาชาด

Remediation:

  1. ลบ @Public()
  2. จำกัด MIME types (image/pdf เท่านั้น)
  3. จำกัด extension whitelist
  4. Scan ด้วย antivirus (ClamAV)
  5. Store ใน object storage (GCS/S3) ไม่ใช่ disk

S9 — body: any ใน DTOs — ไม่มี validation

Severity: Medium

Files:

  • studyLeave.controller.ts:25@Body() body: any
  • (multiple controllers)

Impact:

ValidationPipe global ไม่ทำงานเพราะไม่มี DTO class → ทุก payload ผ่านเข้ามาโดยไม่ validate type/format/range

Remediation:

  1. สร้าง DTO class สำหรับทุก endpoint
  2. ใช้ class-validator decorators (@IsString(), @IsInt(), @MinLength())
  3. เปิด whitelist: true, forbidNonWhitelisted: true ใน ValidationPipe

S10 — CORS origin: '*'

Severity: Medium

Files:

  • main.ts:40app.enableCors({ origin: '*' })

Impact:

Cross-origin request จาก domain ใดก็ได้ → ถ้ามี auth ที่ใช้งานได้ ก็ยังเสี่ยง credential leak ผ่าน browser

Remediation:

  1. ตั้ง origin เป็น allowlist (['https://vtrc.redcross.or.th', 'https://uat-vtrc...'])
  2. ถ้าจำเป็นต้องใช้ cookie — credentials: true + explicit origin

S11 — ทุก connection ล็อกอินด้วย sa

Severity: High

Files:

  • app.module.ts:30-50 — 3 connections (main, vtrc, hrmi) ทั้งหมด user sa
  • .env:18,28,38DB_USER_* = sa

Impact:

sa คือ MSSQL superuser — ทุก operation ทำได้รวมถึง xp_cmdshell, sp_configure, file system access ผ่าน SQL Server

Remediation:

  1. สร้าง dedicated DB user สำหรับ benefit-api (read/write limited tables)
  2. Apply principle of least privilege ที่ GRANT/DENY
  3. Disable xp_cmdshell ใน production

S12 — nmap ติดตั้งใน Docker image

Severity: Low

Files:

  • Dockerfile:18RUN apk add --no-cache nmap ...

Impact:

nmap เป็น network scanner — ถ้า attacker ได้ RCE ใน container จะใช้เป็น pivot tool scan internal network ได้สะดวก

Remediation:

ลบ nmap ออกจาก Dockerfile — เป็น debug tool ไม่จำเป็นต่อ runtime


S13 — Swagger UI mounted publicly ใน production

Severity: Low

Files:

  • main.ts:35SwaggerModule.setup('benefit-api/docs', app, document) โดยไม่มี env check

Impact:

Production expose API documentation สาธารณะ → leak endpoint topology

Remediation:

ts
if (process.env.NODE_ENV !== 'production') {
  SwaggerModule.setup(...);
}

S14 — Node version skew (CI vs runtime)

Severity: Low

Files:

  • bitbucket-pipelines.yml:5 — CI image node:16
  • Dockerfile:1 — runtime node:20-alpine

Impact:

Test ใน CI อาจผ่าน แต่ runtime มี behavior ต่าง (Native modules, V8 features) → silent bug

Remediation:

align Node version — CI และ runtime ต้องตรงกัน


สรุป severity count

SeverityCount
Critical6 (S1, S3, S4, S5, S6, + aggregate)
High4 (S2, S7, S8, S11)
Medium2 (S9, S10)
Low3 (S12, S13, S14)
Total15

Remediation priority (Top 5)

  1. S4 — Remove .env from git, bake image fix → ปิด vector รั่วซึมหลัก
  2. S1 — Rotate JWT secret + remove from history
  3. S3 — Rotate SFTP password + externalize
  4. S5 — Parameterize all raw SQL (จุดที่ใหญ่ที่สุด — ต้องทยอยทำ)
  5. S6 — Remove @Public() จาก mutation endpoints

หลังจาก Top 5 → S11 (DB user), S7 (SSRF), S8 (upload)


ข้อแนะนำเชิงกระบวนการ

  1. เพิ่ม SAST (Static Application Security Testing) — SonarQube, Semgrep, CodeQL ตรวจ raw SQL pattern, hardcoded credential
  2. เพิ่ม dependency scannpm audit, Snyk, Dependabot
  3. เพิ่ม secret scan — GitGuardian, TruffleHog ใน pre-commit และ CI
  4. External pentest — อย่างน้อยปีละครั้ง หลังแก้ Critical
  5. Threat model — ก่อนเพิ่ม feature ใหม่

ไปต่อ