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 unknownMitigation: 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 spreadMitigation: 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 analyzedMitigation: Use inline objects. Apply TypeScript types that exclude sensitive fields.