Skip to content

auth.jwt.weak-secret

JWT signing or verification uses a hard-coded secret. Anyone who reads

OAuthLint idAUTH-JWT-002
SeverityERROR
LLM prevalenceHIGH
CWECWE-798
OWASPAPI2:2023
Languagesjavascript, typescript
Technologiesjsonwebtoken

Why this matters

JWT signing or verification uses a hard-coded secret. Anyone who reads the source (including via leaked GitHub commits) can forge valid tokens.

Move the secret to an environment variable or secret manager, and ensure the value is at least 256 bits (32 ASCII characters) when using HMAC-based algorithms (HS256/HS384/HS512).

Real-world incident: 24,008 unique secrets were found in MCP config files and 3.2% of Claude Code commits leaked secrets in GitGuardian's 2026 report.

❌ Vulnerable

ts
import jwt from 'jsonwebtoken';

// ruleid: auth.jwt.weak-secret
export const token1 = jwt.sign({ uid: 1 }, 'secret');

// ruleid: auth.jwt.weak-secret
export const token2 = jwt.sign({ uid: 2 }, 'changeme');

// ruleid: auth.jwt.weak-secret
export function verifyBad(t: string) {
  return jwt.verify(t, 'mySecret');
}

import { sign } from 'jsonwebtoken';
// ruleid: auth.jwt.weak-secret -- destructured import
export const token3 = sign({ uid: 3 }, 'secret');

✅ Safe

ts
import jwt from 'jsonwebtoken';

// ok: auth.jwt.weak-secret
export const token = jwt.sign({ uid: 1 }, process.env.JWT_SECRET!);

// ok: auth.jwt.weak-secret
export function verifyGood(t: string) {
  return jwt.verify(t, process.env.JWT_PUBLIC_KEY!, { algorithms: ['RS256'] });
}

Suppressing this rule (when you really must)

ts
// oauthlint-disable-next-line auth.jwt.weak-secret -- <reason>
thisLineWouldOtherwiseTriggerTheRule();

Disable directives are line-scoped by design — wholesale silencing of a rule across the codebase is intentionally not supported, because the next reviewer needs to see exactly which lines opted out.

References

Released under the MIT License · powered by Semgrep