Skip to content

4 · Auth mechanism failures

ช่องโหว่ประเภท “มี auth ในชื่อ แต่ไม่มีในความจริง” — Deep Redo แยก backdoor/bypass, @Public() / commented guards, และ broken verify design (ไม่ใช่แค่ secret หลุด)

English: Auth failures = intentional bypasses + accidental public surfaces + broken verification logic. Distinct from secret leakage (Chapter 3), though they compound.

กลับ Blast radius · ถัดไป 90-day plan


4.1 Taxonomy

ClassDefinitionExample IDs
A · Intentional backdoorHardcoded credentials or magic tokens in sourceSEC-2WAYAPI-01, SEC-2WAYVTRC-01, SEC-CU-04, SEC-RCWEB-03
B · Guard present but skippedGlobal JWT + @Public() / commented middlewareSEC-2WAYAPI-04, CRIT-03, SEC-MTVTRC-05, SEC-BENEFIT-03
C · Verify broken / incompleteDecode without verify; ignore expiry; wrong arity; no roleSEC-WORKFLOW-01, SEC-2WAYAPI-05, SEC-MTVTRC-06, SEC-AIRECRUIT-02, CORR-SSO-01
D · Client-side only “auth”Token in URL/localStorage; soft checksSEC-VTRC-RC-02, SEC-VTRC-WEB-01, SEC-2WAYAPI soft Bearer on lists

4.2 Class A — Intentional backdoors / bypass strings

A1 · SEC-2WAYAPI-01 — i3admin → JWT admin 48h

  • Where: 2way-api/src/modules/auth/auth.service.ts (branch: prod):184-240
  • Route: POST …/auth/ is @Public() (auth.controller.ts:13-28)
  • Behavior: Hardcoded username/password; no NODE_ENV gate; issues real JWT with SECRET_JWT_TWOWAY; mockProfile.level = 1 skips org filters
  • Failure mode: Authentication replaced by a shared password in git
  • Fix: Delete block; audit adm9999; rotate TWOWAY secret (Runbook R02)

A2 · SEC-2WAYVTRC-01 / 01b"testnaja"

  • Where: checkbypasstoken.ts:24; checkByPassTokenLine.ts:30 (prod)
  • Behavior: Middleware returns success when token equals static string — skips JWT verify for admin and LINE LIFF surfaces
  • Failure mode: Knowledge of source code = full auth bypass
  • Fix: Remove both conditions (Runbook R03)

A3 · SEC-CU-04/auth/signinbypass

  • Where: cu-central-api/main-api/src/routes.js
  • Behavior: Passwordless signin still mounted
  • Failure mode: Identity without credential proof
  • Fix: Remove from UAT/prod (Runbook R04)

A4 · SEC-RCWEB-03 — OTP 111111

  • Where: recruitment-web/src/components/header/index.js:230-231 (prod)
  • Behavior: Client accepts constant OTP in production bundle
  • Failure mode: Second factor is theater
  • Fix: Strip from prod builds; server must reject constant OTP regardless of client
IDBehaviorEvidence
SEC-RECRUIT-04reCAPTCHA bypass when token omittedrecruit scorecard High
SEC-RECRUIT-05x-refresh-token header bypasses Passport validaterecruit scorecard High
SEC-CU-05ignoreAuth with apiKey onlydirectives/auth.js

4.3 Class B — Guards skipped (@Public() / comments)

B1 · NestJS global guard + @Public() epidemic

Pattern from 2way-api scorecard:

typescript
// libs/jwt/jwt-auth.guard.ts:15-24
if (isPublic) {
  return true;
}
return super.canActivate(context);
IDRepoWhat is publicEvidence
SEC-2WAYAPI-042way-apiMutations + LINE pushannounces.controller.ts:45,56,79,85; complaints.controller.ts:13-114; line.controller.ts:13-34
SEC-2WAYLINE-012way-line-serviceAll LineControllerline.controller.ts:13,19,25,31
SEC-BENEFIT-03benefit-apiMedical claim CRUDwdhospitals.controller.ts:24-40
SEC-BENEFIT-04benefit-apiBatch HRMI import/updatemanualBatch.controller.ts:13-34
SEC-BENEFIT-05benefit-apiUpload/downloadupload.controller.ts:13-22
SEC-PMS-01pms-apiEmployee salarybudget.controller.ts:49-53 (uat)
SEC-RECRUIT-03recruitment-apiBlacklist importVol 12.5
SEC-COMMON-01vtrc-commonFile/address/bank controllersscorecard Critical
CRIT-03centralize-api100% of routesVol 12.3

ไทย: การมี JwtAuthGuard global ทำให้ผู้พัฒนาเชื่อว่า “มี auth แล้ว” — Deep Redo แสดงว่า mutation สำคัญถูก override เป็น public

B2 · Express middleware commented out

IDRepoEvidenceEffect
SEC-MTVTRC-05meeting-vtrc-apiapp.ts:104-120 entire /meeting/secure/* chain commentedAlmost all “secure” API open
SEC-2WAYVTRC-022way-vtrc-apiAuth on getProfile commentedPII by lineID
SEC-2WAYAPI-082way-apiEmpCodeMiddleware next() only :33-49Employees open proxy
SEC-VTRC-API-03vtrc-api/webdeployment no middlewareShell trigger

B3 · Path name lies

PathRealityID
/meeting/secure/*No global authSEC-MTVTRC-05
/meeting/secure/health-check/emailSends real email, no authSEC-MTVTRC-03 · health-check.ts:9-17
@Public() “for testing” left in prodWrite/PII openbenefit/pms/recruit cluster

4.4 Class C — Broken verification design

C1 · SEC-WORKFLOW-01 — decode without verify + role from DB name

  • Evidence: workflow-api/src/main.ts (uat):9-82 — no useGlobalGuards
  • Evidence: jwt-actor.util.ts:1-4,33-47decodeJwtPayloadUnsafe splits JWT, base64-decodes payload, no jwt.verify
  • Evidence: ipd-user.util.ts:20-65inferHospitalUserRole(sourceDB) maps DB name → SOMDEJ/SABC/CHULA
  • Swagger admits signature not verified (main.ts:42 per scorecard)
  • Failure mode: Attacker crafts Bearer with chosen sourceDB → hospital role impersonation
  • Fix: Real JWT verify; server-side role source (Runbook R18)

C2 · SEC-2WAYAPI-05ignoreExpiration: true

  • Evidence: auth.service.ts:31-40 (also ~290, ~339-357)
  • Contrast: JwtStrategy uses ignoreExpiration: false (libs/jwt/jwt.strategy.ts:13-14)
  • Failure mode: Expired VTRC/other tokens can be exchanged for fresh two-way tokens via redirect/verify paths
  • Fix: ignoreExpiration: false; proper refresh policy

C3 · SEC-MTVTRC-06 — bypass middleware cannot verify

  • Evidence: checkbypass.ts:38-78 builds GraphQL query but never vtrcConnector.post
  • Evidence: jwtdecode.ts:12-19 requires (token, secret) but called with one arg
  • Evidence: Missing Bearer still next() (checkbypass.ts:28-30)
  • Failure mode: Re-enabling commented global guard without this fix recreates a false sense of security
  • Fix: Mirror checkbypasswhenredirect.ts; reject empty Bearer

C4 · SEC-AIRECRUIT-02 — signature only

  • Evidence: ai-recruitment-api/middleware/verifyJWT.js:7-40 (prod)
  • Failure mode: Combined with shared SECRET_KEY (Chapter 3), any forged Domain JWT can burn billable AI
  • Fix: Role/scope + rate limit + separate secret

C5 · CORR-SSO-01 — JWT issuance commented; JwtService not injected

  • Evidence: sso-api/src/app.service.ts:27,143-191,220-223; app.controller.ts:36-51
  • Severity in scorecard: High (not Critical) — but auth flow is broken: signIn does not issue JWT; endpoints behind JwtAuthGuard (signOut, verifySession, request2FA, verify2FA) cannot work as designed after signIn
  • Failure mode: Design says 2FA exists; implementation cannot complete session lifecycle
  • Fix: Decide IdP of record; inject JwtService or stop advertising guarded 2FA until fixed (Vol 12.5 ROI item 5)

C6 · SEC-2WAYVTRC-09 — passport done(null, true) always

  • Evidence: middleware/jwtpassport.ts:9-12
  • Failure mode: Any JWT signed with SECRET passes without binding user
  • Fix: Validate claims / load user; reject unknown subjects

C7 · SEC-VTRC-API-02 / SEC-CU-01 / SEC-2WAYAPI-02 — defaults that defeat verify

If verification uses a known default ('secret', empty string, or committed fallback), “verify” still accepts attacker-signed tokens. Treat as auth failure class C even when coded as jwt.verify.

C8 · Chat webhook / WebSocket

IDFailureEvidence
SEC-CHAT-02WS chat no guard; trust fromline from clientchat.gateway.ts:19-26,49-67
SEC-CHAT-03LINE webhook @Public without signatureapp.controller.ts:18-22

4.5 Class D — Client-side session theater

IDMechanismWhy server auth still needed
SEC-VTRC-RC-02/pathLogin/:token/:refreshTokenTokens in history/logs/Referer
SEC-BONEW-01Cross-origin URL tokensSame + extra hop
SEC-VTRC-RC-05 / SEC-VTRC-WEB-01localStorageXSS (SEC-VTRC-RC-04, SEC-VTRC-WEB-02) steals session
SEC-2WAYMTBO-01 / 02localStorage + client route guard onlyBundle still ships “protected” pages
Soft Bearer on 2way announce listOptional Authorization + isMobileRoute remains @Public()QUAL-2WAYAPI-07

ไทย: การเก็บ token ใน localStorage ไม่ใช่ “bug เล็ก” เมื่อคู่กับ XSS ไม่มี CSP — เป็น session theft pipeline


4.6 Auth failure interaction map

Class A backdoor ──issues──► real JWT (Class C still verifies signature)

        └── uses Cluster C/A secrets (Chapter 3)

Class B @Public ──skips──► Class C entirely (no token needed)

Class C ignoreExpiration ──extends──► lifetime of Class A tokens

Class D XSS ──steals──► tokens issued after “successful” Class C verify

CORR-SSO-01 ──breaks──► intended 2FA while other services still trust SSO-shaped flows

4.7 Detection / audit queries for operators

อย่าใส่ secret ในคำสั่ง — ใช้เพื่อหา pattern ในซอร์ส บน canonical branch:

bash
# Backdoors / magic strings (from known IDs)
rg -n "i3admin|testnaja|signinbypass|111111" --glob '!**/node_modules/**'

# Nest public surface
rg -n "@Public\\(" --glob '**/*controller*.ts'

# Commented secure mounts (meeting)
rg -n "meeting/secure" meeting-vtrc-api/app.ts

# ignoreExpiration
rg -n "ignoreExpiration:\\s*true"

# Unsafe JWT decode helper names
rg -n "decodeJwtPayloadUnsafe|ignoreAuth"

Access-log hunts suggested by scorecards:

HuntWhy
empCode=adm9999 / username i3adminSEC-2WAYAPI-01
Requests with weird Bearer equal to bypass stringsSEC-2WAYVTRC-01
Unauthenticated hits to /budget/getEmployeeSalarySEC-PMS-01
/wdHospitals/add without AuthorizationSEC-BENEFIT-03
/auth/signinbypassSEC-CU-04

4.8 Acceptance criteria for “auth fixed” (platform)

Auth is not fixed until all are true for a service:

  1. No Class A strings/routes on prod branch
  2. No Class B @Public() on write/PII (inventory signed off)
  3. Class C: verify signature + expiry + server-side authorization
  4. Secrets not defaulted (Chapter 3)
  5. Frontend does not put tokens in URLs (Class D)

Meeting-specific: SEC-MTVTRC-05 and SEC-MTVTRC-06 both closed — either alone is insufficient.


ServiceScorecard pathAuth dimension status (from scorecard)
benefit-apivolume-06-domain-microservices/benefit-api/06-scorecard.mdAuth pipeline 🔴 — @Public medical/batch/upload
2way-apivolume-05-2way-platform/2way-api/04-scorecard.mdAuth pipeline แดง — backdoor + @Public + ignoreExpiration
meeting-vtrc-apivolume-07-meeting-platform/meeting-vtrc-api/04-scorecard.mdGlobal auth commented + bypass broken
vtrc-apivolume-03-legacy-core/vtrc-api/10-scorecard.mdCommand injection + unauth deploy + default JWT
sso-apivolume-06-domain-microservices/sso-api/04-scorecard.mdCORR-SSO-01 High — issuance dead

ไป 5 · 90-day security plan