Interlace ESLint
ESLint Interlace

no-sensitive-payload

**Severity:** � Medium

Prevent storing sensitive data in JWT payload which is only base64-encoded

Severity: 🟡 Medium
CWE: CWE-359

Rule Details

JWT payloads are NOT encrypted, only base64-encoded. Anyone can decode and read the payload contents. Sensitive data like passwords, PII, or financial information should never be stored in JWT payloads.

Detected Sensitive Fields

  • Passwords: password, passwd, pwd, secret
  • PII: email, phone, ssn, address, dob
  • Financial: creditCard, cardNumber, cvv, bankAccount
  • Tokens: accessToken, refreshToken, apiKey
  • Supports camelCase, snake_case, and kebab-case variants

Examples

❌ Incorrect

jwt.sign({ password: 'secret123' }, secret);
jwt.sign({ email: 'user@example.com' }, secret);
jwt.sign({ ssn: '123-45-6789' }, secret);
jwt.sign({ credit_card: '4111111111111111' }, secret);

✅ Correct

// Store sensitive data server-side, reference by ID
jwt.sign({ sub: 'user-id-123', role: 'admin' }, secret);
jwt.sign({ userId: 'abc123', permissions: ['read'] }, secret);

Options

{
  "jwt/no-sensitive-payload": ["error", {
    "additionalSensitiveFields": ["customSecret", "internalId"]
  }]
}

Known False Negatives

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

Computed Property Names

Why: The rule checks literal property names; computed properties are not resolved.

// ❌ NOT DETECTED - Dynamic property name
const field = 'password';
jwt.sign({ [field]: 'secret123' }, secret); // Property name unknown

Mitigation: Avoid computed property names in JWT payloads. Use TypeScript interfaces.

Spread Operator with Sensitive Data

Why: Spread objects hide their properties at lint time.

// ❌ NOT DETECTED - Sensitive data in spread object
const user = { name: 'John', ssn: '123-45-6789' };
jwt.sign({ ...user, role: 'admin' }, secret); // SSN hidden in spread

Mitigation: Explicitly pick/omit fields before signing. Use pick() utilities.

Nested Sensitive Data

Why: The rule checks top-level properties by default.

// ❌ NOT DETECTED - Sensitive data in nested object
jwt.sign(
  {
    sub: '123',
    profile: { email: 'user@example.com' }, // Nested - not detected
  },
  secret,
);

Mitigation: Configure checkNestedProperties: true if available. Flatten sensitive checks in code review.

Obfuscated Field Names

Why: Field name patterns don't match intentionally obfuscated names.

// ❌ NOT DETECTED - Obfuscated field name
jwt.sign(
  {
    sub: '123',
    e: 'user@example.com', // 'e' for email - not in pattern
    p: '555-1234', // 'p' for phone - not in pattern
  },
  secret,
);

Mitigation: Use additionalSensitiveFields to add custom patterns.

Variable Payload Reference

Why: Variable contents are not tracked.

// ❌ NOT DETECTED - Payload from variable
const payload = { ssn: '123-45-6789' };
jwt.sign(payload, secret); // Variable not analyzed

Mitigation: Use inline objects. Apply TypeScript types that exclude sensitive fields.

Further Reading

On this page