4.8 · Security + debt
บทนี้ catalog ความเสี่ยงด้านความปลอดภัยและ technical debt ของ cu-central-api โดยเน้นสิ่งที่ต่างจาก vtrc-api (ส่วนที่เหมือนกัน cover ใน Volume 9 แล้ว)
Severity summary
| Severity | Count |
|---|---|
| 🔴 Critical | 4 |
| 🟡 High | 6 |
| 🟢 Medium | 4 |
🔴 Critical
C1 · JWT_SECRET default empty string
JWT_ISSUER = '',
JWT_SECRET = '',const getPrivayeKey = () => {
return JWT_SECRET
}🔴 default เป็น empty string — และ jsonwebtoken@8.5.1 ยอม sign ด้วย empty string (ไม่ throw)
ถ้า env unset ใน deployment ใด — service จะ boot และ sign token จริงด้วย secret "" ทำให้ทุกคนสามารถ forge JWT ได้
ไม่มี guard แบบ if (IS_PRODUCTION && !JWT_SECRET) throw ที่จะ hard-fail ที่ boot
typo getPrivayeKey (ควรเป็น getPrivateKey) — ไม่มี impact แต่บ่งบอกว่าไม่มี code review ที่จริงจัง
C2 · apiKeys hardcoded ใน code
const allowedApiKeys = [
{
apiKey: 'OGJlYTEzOTBjMzk3NjlmYWQ1ZTcxZDZhN2NhZWMyYTk3YTQ2NGIzMDhiMGM4NmRjM2NlNjZkM2I2ZDIyOGQyMQ',
...
},
{
apiKey: '562B132B1E87695A052717C20336CDA57FBC4C5B19EF31356CC07C5A43656DE47DEFE452D241C3F443DE54',
...
}
](apiKeys ที่ commit — detail ในบท 4.4)
C3 · .env.develop-test committed
ไฟล์ .env.develop-test ที่มี UAT secrets ทั้งหมด commit อยู่ใน repo — cover ใน Volume 9.5 (cu-central-api inventory)
C4 · axois typosquat
"axois": "0.0.1-security",🔴 typosquat ของ axios — npm reserve 0.0.1-security สำหรับ typosquatted packages
- codebase ไม่ได้ import
axoisจริง (grep confirms 0 imports) - แต่
npm installยัง pull package เข้าnode_modules - ถ้า typosquat มี postinstall script → execute ตอน install
C5 · httpRequest.js ใช้ axios ที่ไม่ได้ declare
import axios from 'axios'const result = await axios(options)- import
axiosแต่ dependencies มีแค่axois(typo) - ถ้ามี code path ใดถึง
httpRequest.js— crash ทันที
🟡 High
H1 · error-ex git URL dependency
"error-ex": "git+https://github.com/Qix-/node-error-ex.git",🟡 pin ไปที่ git URL แทน npm version — mutable ref ไม่มี hash protection ถ้า upstream repo compromised
H2 · ldapjs unmaintained
"ldapjs": "^2.2.0",🟡 project archived ตั้งแต่ 2023 — fork ldapts เป็น successor ที่ active
H3 · jsonwebtoken@8 CVE
"jsonwebtoken": "^8.5.1",🟡 8.x EOL — CVE-2022-23529 family มี patch ใน v9+
H4 · rejectUnauthorized: false ใน LDAP
const tlsOptions = {
rejectUnauthorized: false,
}🟡 ปิด TLS cert verification บน LDAP connection — MITM สามารถ intercept password ของ user
H5 · Node 12 + esm shim
FROM node:12.18.3"esm": "^3.2.25",🟡 Node 12 EOL April 2022 — 4+ ปีไม่มี security patch
esm shim (last release 2019) เป็น root cause ที่ lock codebase ไว้ที่ Node 12
H6 · xlsx CVE history
"xlsx": "^0.16.9",🟡 SheetJS community มี prototype pollution CVE history — เวอร์ชันปัจจุบันอยู่บน SheetJS CDN (ไม่ใช่ npm)
🟢 Medium
M1 · clockTolerance ใน JWT payload (misuse)
return { ...payload, clockTolerance: 5 }clockTolerance เป็น jwt.verify option — ใส่เป็น payload claim ไม่มีผล เป็น misuse ของ jsonwebtoken API
M2 · signinDomains hardcoded
resolvers/authentication.js:35-72 — 26 rows ของ org list hardcoded ใน code
การเพิ่ม/แก้ org ต้อง deploy code ใหม่ — ควรย้ายไป masterData table
M3 · dbHRMI_test dead connection
const dbHRMI_test = new Sequelize("dbHRMI_test", ...)connection pool ที่เปิดขึ้นทุก boot แต่ไม่มี model ผูก — เปลือง MSSQL resource
M4 · OTP inconsistency
hr/hrAddress.js ปิด OTP ตั้งแต่ 8 Jul 2021 แต่ hr/hrPhoneNumber.js ยังทำงาน — sibling resolvers ใช้ pattern ต่างกัน
Audit cross-reference
mapping กับ audit/05-tech-debt-register.md
| Debt ID | Chapter | Topic |
|---|---|---|
| SEC-7 | C4, C5 | axois typosquat + httpRequest.js broken |
| SEC-8 | C3 | .env.develop-test committed |
| SEC-9 | H1 | error-ex git URL |
| LEG-1 | H5 | Node 12 EOL |
| LEG-2 | H5 | esm shim lock-in |
| LEG-3 | (Volume 3) | apollo-server v2 EOL |
| LEG-8 | H2 | ldapjs unmaintained |
| LEG-9 | H3 | jsonwebtoken v8 CVE |
| LEG-27 | H6 | xlsx prototype pollution |
| LEG-28 | — | sha512crypt-node (ใช้ใน activeDirectory.js:5) |
| CORR-9 | C5 | httpRequest.js broken |
Comparison กับ vtrc-api security
| Aspect | vtrc-api | cu-central-api |
|---|---|---|
| JWT secret default | 'secret' | '' (empty) |
| apiKeys | ใน config.js (7 prod) | hardcoded 2 keys |
| Password handling | forward plaintext ไป centralize | verify ผ่าน LDAP โดยตรง |
execSync injection | 🔴 5 endpoints | 🟢 ไม่มี |
| SQL injection | 🔴 statLog.js | 🟢 ไม่พบ |
| CORS bug | 🔴 corss typo | 🟢 ไม่มี (ไม่ได้ set CORS) |
| introspection prod | 🔴 true เสมอ | 🟢 gate ด้วย IS_PRODUCTION |
| File upload | 🔴 multer no filter | 🟢 ไม่มี file upload endpoint |
| Rate limit | 🔴 ไม่มี | 🔴 ไม่มี |
cu-central-api มี security posture ที่ดีกว่า vtrc-api ในหลายด้าน (ไม่มี execSync, ไม่มี SQLi, gate introspection ถูก) แต่แย่กว่าในเรื่อง LDAP TLS และ ignoreAuth bypass
Modernization path
ตาม audit/05-tech-debt-register.md §5.3 cu-central-api ทั้งหมดควร decompose เป็น 2 Go services
cu-central-api (Node 12)
│
▼
┌──────────────────┐ ┌──────────────────┐
│ identity-service │ │ hr-data-service │
│ (Go + Hex/DDD) │ │ (Go + Hex/DDD) │
└──────────────────┘ └──────────────────┘
│ │
│ ├──▶ MSSQL (per-shard connection)
│ ├──▶ HRMI HTTP API
│ └──▶ HIS API
│
├──▶ LDAP/AD (go-ldap)
├──▶ Redis (go-redis)
├──▶ JWT (golang-jwt/jwt/v5)
└──▶ Casbin (RBAC policy)| Context | เหตุผลที่แยก |
|---|---|
identity-service | auth + RBAC + session เป็นส่วนงานที่ใช้ร่วมกันทุก service — ควรแยกเป็น service เดียวที่ทุกคนเรียก |
hr-data-service | การอ่าน/เขียน HR data เป็นงานเฉพาะตัว — มี SourceDB sharding เป็นรายละเอียดภายใน |
รายละเอียดเพิ่มเติมใน Volume 15
Wave 0 stop-the-bleed checklist
| # | Action | Severity | Effort |
|---|---|---|---|
| 1 | Rotate JWT_SECRET + hard-fail at boot ถ้า empty | 🔴 C1 | 0.5d |
| 2 | Rotate hardcoded apiKeys + ย้ายไป env | 🔴 C2 | 1d |
| 3 | Purge .env.develop-test + rotate secrets | 🔴 C3 | 1-2d |
| 4 | ลบ axois typosquat + add axios@^1.x | 🔴 C4, C5 | 0.5d |
| 5 | Fix rejectUnauthorized: false ใน LDAP | 🟡 H4 | 1d |
รวม Wave 0 ≈ 4-5 วัน — ลด Critical ทั้งหมด
อ่านต่อ → 4.9 Scorecard + comparison