Skip to main content
ESLint Interlace
Plugin: jwtRules

no-weak-secret

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

Require strong secrets (256+ bits) for HMAC-based JWT signing

Severity: 🟠 High
CWE: CWE-326

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

⚠️ CWE-326 OWASP:A04 CVSS:5.9 | Inadequate Encryption Strength detected | MEDIUM
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A04_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-326 OWASP:A04 CVSS:5.9
Issue DescriptionSpecific vulnerabilityInadequate Encryption Strength detected
Severity & ComplianceImpact assessmentMEDIUM
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Rule Details

This rule detects weak secrets used for HMAC-based JWT signing. Weak secrets can be brute-forced offline, allowing attackers to forge tokens.

Examples

❌ Incorrect

// Known weak patterns
jwt.sign(payload, 'secret');
jwt.sign(payload, 'password');
jwt.sign(payload, 'changeme');

// Short secrets (<32 characters)
jwt.sign(payload, 'shortkey');

✅ Correct

// Environment variable
jwt.sign(payload, process.env.JWT_SECRET);

// Long secret (32+ characters)
jwt.sign(payload, 'ThisIsAVeryStrongSecretThatIs32Ch+');

// Generated secret
jwt.sign(payload, crypto.randomBytes(32).toString('hex'));

Options

{
  "jwt/no-weak-secret": ["error", {
    "minSecretLength": 32
  }]
}
OptionTypeDefaultDescription
minSecretLengthinteger32Minimum secret length (chars)

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Variable References

Why: Only literal string secrets are analyzed for length/weakness.

// ❌ NOT DETECTED - Secret from variable
const weakSecret = 'abc';
jwt.sign(payload, weakSecret); // Variable not analyzed

Mitigation: Use environment variables. Apply TypeScript branded types for validated secrets.

Concatenated Secrets

Why: String operations are not evaluated statically.

// ❌ NOT DETECTED - Concatenated weak parts
const part1 = 'short';
const part2 = 'key';
jwt.sign(payload, part1 + part2); // Still weak, but not detected

Mitigation: Use single, long secret strings. Avoid dynamic secret construction.

Encoded Weak Secrets

Why: Base64/hex encoding can hide a weak underlying secret.

// ❌ NOT DETECTED - Encoded but short underlying value
const encoded = Buffer.from('weak').toString('base64'); // 'd2Vhaw=='
jwt.sign(payload, encoded); // String is long enough but entropy is low

Mitigation: Validate secret entropy at runtime. Use cryptographically random secrets.

Environment Variable Values

Why: The rule trusts environment variables but cannot verify their actual values.

// ❌ NOT DETECTED - Weak secret in env var
// .env: JWT_SECRET=password
jwt.sign(payload, process.env.JWT_SECRET); // Trusts the env var

Mitigation: Use secret rotation policies. Validate secret strength at application startup.

Secrets from Configuration Files

Why: Values loaded from config files are treated as safe.

// ❌ NOT DETECTED - Weak secret in config
import { JWT_SECRET } from './config'; // Config contains 'weak123'
jwt.sign(payload, JWT_SECRET);

Mitigation: Apply secret validation at config load time. Use environment variables for secrets.

Further Reading

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.