1 · Platform attack surface map
แผนที่พื้นผิวโจมตีระดับแพลตฟอร์มหลัง Deep Redo — จัดตาม entry point ที่ attacker แตะได้โดยไม่ต้องมี identity จริง หรือด้วย identity ที่ forge ได้จาก secret ที่หลุด
English: Attack surface map = unauthenticated / weakly authenticated paths + shared-secret forge paths. Every row cites a scorecard ID and file:line.
กลับ index · ถัดไป Top-20 runbook
1.1 How to use this map
| Column | Meaning |
|---|---|
| Surface | What is exposed (route / decorator / middleware) |
| Auth reality | What the code actually enforces |
| Blast | What data or privilege is reachable |
| Evidence | Scorecard ID + path |
สมมติฐานที่ระบุในหลาย scorecard: WAF/gateway ด้านนอกอาจลดความรุนแรงในทางปฏิบัติ แต่โค้ดยังเป็น auth bypass — ถือว่า Critical จนกว่าจะมีหลักฐาน control ภายนอก (ดู 2way-api/04-scorecard caveat)
1.2 Legacy core — GraphQL + REST side doors
vtrc-api (canonical docs: Vol 3.1.10)
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
PDF encrypt pipeline execSync(qpdf …) | Auth may exist on some routes; password/path enter shell string | RCE on API host | SEC-VTRC-API-01 · vtrc-api/api/src/routes.js:73,120,227,277,355 |
Default JWT_SECRET = 'secret' | Forge JWT if env missing | Impersonate any user | SEC-VTRC-API-02 · config.js:12 |
GET …/webdeployment | No apiKey/JWT | Trigger shell binary deploy | SEC-VTRC-API-03 · routes.js:975-984 |
GET /file/:id/download | No apiKey | Confidential file download if id known | SEC-VTRC-API-04 · routes.js:464 |
Raw SQL in statLog cron | Trusted? / indirect | SQLi if untrusted rows | SEC-VTRC-API-07 · lib/controllers/statLog/statLog.js |
CORS typo corss | Allowlist dead → open origins | CSRF / browser abuse assist | SEC-VTRC-API-06 · index.js:76-91 |
ไทย: vtrc-api เป็นจุดรวมของ legacy frontends — command injection + unauth deploy + default JWT เป็น combo ที่ IR ต้อง treat เป็น P0 พร้อม secrets cluster
cu-central-api
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
/auth/signinbypass | Passwordless signin live | Account takeover without password | SEC-CU-04 · routes.js (Vol 12.3 / 12.5) |
| Hardcoded device apiKeys | Source = key | Device-key gate bypass | SEC-CU-02 · lib/auth/auth.controller.js |
JWT_SECRET empty default | Forge with empty key | Session forge | SEC-CU-01 · config.js, jwtToken.js |
.env.develop-test in git | UAT secrets in history | UAT DB / JWT | SEC-CU-03 |
ignoreAuth + apiKey only | No user JWT | Resolvers marked ignoreAuth open to anyone with device key | SEC-CU-05 · directives/auth.js |
LDAP rejectUnauthorized: false | MITM LDAP | Credential intercept | SEC-CU-06 · activeDirectory.connector.js |
centralize-api (NestJS REST)
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
All controllers @Public() | Auth machinery dead (17/17) | Full PII employee surface | CRIT-03, CRIT-04 · Vol 12.3 |
.env committed | DB password + bad SECRET_KEY | DB + forge | CRIT-01, CRIT-02 |
English: NestJS JWT guards exist in the library, but every current route is @Public() — treat centralize-api as an open employee directory unless network ACL is proven.
1.3 Web frontends — token exfil + XSS
| Repo | Surface | Auth reality | Blast | Evidence |
|---|---|---|---|---|
vtrc-rc-backoffice | .env + CRA inline Stimulsoft MSSQL conn | Secret in git and browser bundle | Direct DB string leak | SEC-VTRC-RC-01 · .env; shallow C1/C2 superseded by deep ID |
vtrc-rc-backoffice | /pathLogin/:token/:refreshToken | Token in URL path | History / logs / Referer leak | SEC-VTRC-RC-02 · App/index.js:24 |
vtrc-rc-backoffice | dangerouslySetInnerHTML | No sanitizer | XSS → steal localStorage token | SEC-VTRC-RC-04 |
vtrc-rc-backoffice | Token in localStorage | XSS-readable | Session theft | SEC-VTRC-RC-05 · utils/auth.js |
vtrc-web | localStorage tokens | XSS-readable | Session theft | SEC-VTRC-WEB-01 · utils/auth.js |
vtrc-web | dangerouslySetInnerHTML | No sanitizer | XSS | SEC-VTRC-WEB-02 |
vtrc-web | No CSP / no SRI | Weak browser defenses | XSS impact↑ | SEC-VTRC-WEB-03, SEC-VTRC-WEB-04 |
vtrc-backoffice-new | Token in URL cross-origin | Path segments | Token leak | SEC-BONEW-01 · Login/index.js:93-103 (branch: uat) |
recruitment-web | AES keys in tracked .env* | Bake into CRA bundle | PII decrypt | SEC-RCWEB-01 · .env:2,9, cryptoJs.js:1-32 (branch: prod) |
recruitment-web | OTP constant 111111 | Client bypass | Soft 2FA skip | SEC-RCWEB-03 · header/index.js:230-231 |
ไทย: Frontend ไม่ใช่แค่ “UI debt” — token-in-URL + XSS + localStorage ทำให้ Critical backend ถูกขยายผ่าน session theft
1.4 2way Platform
2way-api (branch: prod) — scorecard 04
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
POST …/auth/ backdoor i3admin | Hardcoded user/pass → JWT admin 48h | Full high-privilege session | SEC-2WAYAPI-01 · auth.service.ts:184-240 |
| SSO AES / SSO password fallbacks | Env missing → literals in source | Decrypt/forge SSO path | SEC-2WAYAPI-02 · auth.service.ts:20-26 |
| Announces/surveys/complaints SQL concat | Params → WHERE string | SQLi (+ many routes @Public) | SEC-2WAYAPI-03 · announces.service.ts:164-463 |
Mutations + LINE @Public() | Global JwtAuthGuard skipped | Write workflows / LINE spam | SEC-2WAYAPI-04 · announces.controller.ts:45+, line.controller.ts |
verifyJwt ignoreExpiration: true | Expired tokens accepted | Session persistence after expire | SEC-2WAYAPI-05 · auth.service.ts:31-40 |
Employees middleware next() only | Open proxy to centralize | Employee PII read | SEC-2WAYAPI-08 · employees.controller.ts:33-49 |
MSSQL encrypt: false | Cleartext DB traffic | Network sniff | SEC-2WAYAPI-06 · app.module.ts:39-42 |
LINE URL http://20.212.116.206:8200 | Hardcoded HTTP IP | MITM / sticky infra | SEC-2WAYAPI-07 · line.service.ts:76,86 |
2way-vtrc-api (branch: prod) — scorecard 04
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
Bypass "testnaja" | Middleware short-circuit | All guarded admin routes | SEC-2WAYVTRC-01 · checkbypasstoken.ts:24 |
| LINE middleware same bypass | Same string | LIFF endpoints | SEC-2WAYVTRC-01b · checkByPassTokenLine.ts:30 |
POST /2way/getProfile | Auth commented out | PII by lineID | SEC-2WAYVTRC-02 · getProfile.ts:34-63,79-133 |
POST /2way/updateIncident | Validator only — no auth middleware | Change incident status unauth | SEC-2WAYVTRC-07 · updateIncident.ts:23 |
Mongo + MSSQL sa in source | Hardcoded | DB takeover | SEC-2WAYVTRC-03 / 03b · mongo.config.ts:2-8, env.ts:17-32 |
SSO creds + AES in loginSSO.ts | Literals | SSO abuse | SEC-2WAYVTRC-08 · loginSSO.ts:21-27 |
2way-line-service
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
LineController all @Public() | No JWT | Fake LINE events if no signature check | SEC-2WAYLINE-01 · line.controller.ts:13,19,25,31 |
1.5 Domain microservices
benefit-api (branch: prod) — scorecard 06
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
.env + SFTP password hardcode | Secrets in git | MSSQL ×3 + JWT + FMIS SFTP | SEC-BENEFIT-01 · .env:18,25,32,64; exportpaymentreport.service.ts:5271,5824,6151 |
Raw SQL empCode = ${…} | No params | SQLi into vtrc schema | SEC-BENEFIT-02 · approvedisaster.service.ts:415-417; payments large ranges |
wdHospitals CRUD @Public() | No login | Medical claim create/read/update | SEC-BENEFIT-03 · wdhospitals.controller.ts:24-40 |
Batch import/update @Public() | No login | HRMI batch corruption | SEC-BENEFIT-04 · manualBatch.controller.ts:13-34 |
Upload @Public() | No login | Malware store / doc leak | SEC-BENEFIT-05 · upload.controller.ts:13-22 |
/ping-centrail?url= | @Public + HTTP ping | SSRF | SEC-BENEFIT-06 · app.controller.ts:13-16 |
recruitment-api / ai-recruitment-api / pms-api
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
recruitment .env + GCP SA | In git | DB + JWT + SMTP + reCAPTCHA + cloud | SEC-RECRUIT-01 · .env + vtrc-gcp-sa-key.json (prod) |
Dynamic EXEC SP string | User params concat | SQLi on ai connection | SEC-RECRUIT-02 · candidate.repositories.ts:1324-1338 |
POST /blacklist/import_blacklist @Public() | No JWT | Open blacklist import | SEC-RECRUIT-03 |
| ai-recruit OpenAI/remove.bg keys | In git | Financial API abuse | SEC-AIRECRUIT-01 · .env:2,5,7,9-10,11 |
| ai-recruit JWT signature only | No role | Billable AI for any JWT holder | SEC-AIRECRUIT-02 · verifyJWT.js:7-40 |
GET /budget/getEmployeeSalary @Public() | No login | Salary/budget by org unit | SEC-PMS-01 · budget.controller.ts:49-53 (uat) |
pms .env shared SECRET_KEY | In git | Forge JWT across domain cluster | SEC-PMS-02 · .env:12,19,26,33,41,45 |
chat / workflow / common / jobsched / sso
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
| chat raw SQL rooms/tags | Query string concat | SQLi | SEC-CHAT-01 · chat.service.ts:103-157, tags.service.ts:55-61 |
chat .env | In git | MSSQL/Mongo/LINE | SEC-CHAT-04 · .env:10-11,26,28-29 |
| chat WS + webhook | No guard / no LINE sig | Spoof chat / LINE | SEC-CHAT-02, SEC-CHAT-03 |
workflow no useGlobalGuards | Decode only + role from sourceDB | Impersonate hospital roles | SEC-WORKFLOW-01 · main.ts:9-82, jwt-actor.util.ts:1-4,33-47 |
workflow .env.example real secrets | Tracked | AI key + DB | SEC-WORKFLOW-02 · .env.example:11,31,44 |
vtrc-common controllers @Public() | Writes open | File/address/bank | SEC-COMMON-01 |
vtrc-common .env + Docker bake | sa + SECRET_KEY | Instance-wide DB | SEC-COMMON-03 · .env:11,17; Dockerfile:19 |
| address SQL concat | Params | SQLi shared lib | SEC-COMMON-02 · address.repository.ts:82-94,117-130 |
job-scheduler .env | sa + RSA secret | Shared with common/chat | SEC-JOBSCHED-01 · .env:9-11,18 |
| job-scheduler cron POST | Unauthenticated outbound | Trigger remote crontab | SEC-JOBSCHED-02 · incidents.service.ts:20-21 (see jobsched 01/02) |
sso-api JWT sign() commented | Guarded endpoints dead after signIn | 2FA/logout broken | CORR-SSO-01 · app.service.ts:27,143-191,220-223 |
1.6 Meeting platform
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
.envdev/.envuat/.envprod | Secrets in git (3 envs) | JWT/SMTP/DB/apiKey | SEC-MTVTRC-01 (branch: prod-meeting-vtrc-api) |
GET /meeting/mockuser/one | No auth | Real PII hardcoded | SEC-MTVTRC-02 · mockuser.ts:29-60, mount app.ts:209-210 |
Global /meeting/secure/* middleware | Entire block commented | Meetings/PDF/email open | SEC-MTVTRC-05 · app.ts:104-120 |
checkBypassMiddleWare | Broken verify (no post; wrong decodeJwt arity) | Guard useless if re-enabled as-is | SEC-MTVTRC-06 · checkbypass.ts:38-78, jwtdecode.ts:12-19 |
| health-check email / corn-email / static files | No auth | Spam + file read | SEC-MTVTRC-03, SEC-MTVTRC-07 |
ไทย: path ชื่อ secure ห้ามเชื่อ — เปิด app.ts ก่อน (scorecard tip)
1.7 Mobile + auxiliary
| Surface | Auth reality | Blast | Evidence |
|---|---|---|---|
| RSA private PEM in RN bundle | Client holds private key | Crypto broken forever for shipped builds | SEC-MOBILE-01 · login.js:16-42; SEC-NEW-01 · RSAKey.ts:11-38 |
| Android keystore passwords in git | Sign APK as org | Malicious update signing | SEC-MOBILE-02 · build.gradle:174-179 |
report-log request_*.log | PII in git | PDPA exposure | SEC-REPORTLOG-01 |
1.8 ASCII topology — unauth / bypass hotspots
[Attacker]
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
pathLogin URL @Public NestJS commented Express
(RC-BO / BO-new) (benefit/pms/…) (/meeting/secure/*)
│ │ │
▼ ▼ ▼
localStorage XSS medical/salary meeting CRUD
+ Stimulsoft .env /blacklist/batch + mockuser PII
│
▼
shared SECRET_KEY cluster
(benefit/recruit/ai/pms)
│
▼
forge JWT → other domain APIs 2way-api backdoor i3admin ──► JWT 48h admin
2way-vtrc "testnaja" ───────► skip middleware
cu-central signinbypass ────► passwordless user
centralize @Public 100% ────► employee REST open1.9 Priority surfaces for IR triage (first 24h)
ถ้ามี incident “มีคนเข้าถึงข้อมูลสวัสดิการ/เงินเดือน/ประชุม” ให้ไล่ตามลำดับนี้ก่อน (อิง Vol 12.5 ROI):
- Shared JWT forge from committed
SECRET_KEY— Chapter 3 - Backdoors still in source — Chapter 4 (
SEC-2WAYAPI-01,SEC-2WAYVTRC-01,SEC-CU-04) @Public()medical/salary/blacklist — Chapter 2 runbook items- Meeting global auth commented —
SEC-MTVTRC-05 - Token-in-URL logs on reverse proxies —
SEC-VTRC-RC-02,SEC-BONEW-01
1.10 Out of scope / UNVERIFIED (explicit)
| Item | Status |
|---|---|
| Live pentest confirmation on UAT/prod | Not claimed here |
| Whether API gateway adds auth outside code | UNVERIFIED in scorecards |
| Mobile/OCR deep agents still in flight | Vol 17.4 — totals not final |
Network ACL to 10.188.128.* MSSQL | Assumed reachable only if network allows — impact still Critical if secrets leaked |