Data Exposure with Jwt Tokens
How Data Exposure Manifests in JWT Tokens
Data exposure in JWT tokens occurs when sensitive information is inadvertently included in the token payload, allowing attackers to extract confidential data without needing to decode or forge tokens. This vulnerability is particularly dangerous because JWTs are often transmitted in HTTP headers, cookies, or URLs, making them accessible through various attack vectors.
The most common manifestation is excessive claims in the JWT payload. Developers frequently include user profile data, internal identifiers, or even database IDs in the token to avoid additional database lookups. While convenient, this practice exposes information that should remain confidential. For example, a token might contain:
{
"sub": "1234567890",
"name": "John Doe",
"email": "[email protected]",
"role": "admin",
"department": "Finance",
"salary": "$120,000",
"ssn_last4": "5678"
}In this example, salary and partial SSN exposure could lead to privacy violations and enable targeted attacks. An attacker who intercepts this token gains immediate access to sensitive personal and financial information.
Another critical manifestation is insecure token transmission. JWTs sent over unencrypted HTTP connections or included in URLs are vulnerable to network interception and browser history logging. Even when using HTTPS, improper implementation can lead to exposure through browser extensions, proxy logging, or server logs that record full request headers.
Weak token signing algorithms represent a third manifestation. Using HS256 with weak secrets or allowing none algorithm acceptance enables attackers to modify token contents and potentially expose data through crafted tokens. Some implementations incorrectly accept unsigned tokens or use predictable secrets, making token forgery trivial.
Token storage vulnerabilities also contribute to data exposure. Storing JWTs in localStorage makes them accessible to cross-site scripting (XSS) attacks, while insecure cookie configurations can lead to cross-site request forgery (CSRF) or exposure through third-party scripts.
JWT-Specific Detection
Detecting data exposure in JWT tokens requires examining both the token structure and implementation patterns. Here are specific detection methods:
Payload Analysis involves decoding the JWT and examining claims for sensitive data. A security scanner should flag tokens containing:
- Personal Identifiable Information (PII): names, emails, phone numbers, addresses
- Financial data: account numbers, balances, transaction details
- Internal system information: database IDs, internal URLs, system architecture details
- Security credentials: API keys, passwords, cryptographic material
- Business logic: role hierarchies, permission structures, workflow states
Algorithm Validation checks ensure tokens use strong signing algorithms (RS256, ES256) and reject weak or none algorithms. Detection should verify:
const jwt = require('jsonwebtoken');
// Vulnerable: accepts none algorithm
jwt.verify(token, publicKey, { algorithms: ['RS256', 'none'] });
// Secure: strict algorithm validation
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
Transmission Security verification ensures tokens are only transmitted over HTTPS and never included in URLs. Detection should identify:
- Tokens in query parameters
- HTTP-only cookie configuration
- Secure flag settings
- SameSite attribute configuration
middleBrick's JWT-specific scanning performs automated detection across these categories in seconds. The scanner examines JWT tokens in API responses, headers, and cookies, identifying excessive claims, weak algorithms, and transmission vulnerabilities without requiring authentication credentials.
The scanner provides a security risk score with specific findings for each JWT-related issue, prioritized by severity with actionable remediation guidance. For example, it might detect a token containing PII and recommend removing those claims while maintaining functionality through database lookups.
JWT-Specific Remediation
Remediating JWT data exposure requires implementing secure token practices and minimizing sensitive information in token payloads. Here are specific remediation strategies:
Minimal Claims Principle dictates including only essential information in JWTs. The standard claims (iss, sub, aud, exp, nbf, iat, jti) should be the only claims present unless absolutely necessary. Instead of including user data directly:
// Vulnerable - excessive claims
const token = jwt.sign({
sub: userId,
name: user.name,
email: user.email,
role: user.role,
department: user.department
}, secret, { expiresIn: '1h' });
// Secure - minimal claims
const token = jwt.sign({
sub: userId,
role: user.role
}, secret, { expiresIn: '1h' });
Secure Token Storage and Transmission prevents exposure through improper handling:
// Secure cookie configuration
res.cookie('jwt', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000
});
// Secure header transmission
res.set('Authorization', `Bearer ${token}`);
Strong Algorithm Implementation ensures token integrity:
const crypto = require('crypto');
// Generate strong RSA key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Sign with RS256
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// Verify with strict algorithm validation
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
Token Rotation and Short Expiration limits exposure window:
// Short expiration with refresh token pattern
const accessToken = jwt.sign(
{ sub: userId, role: user.role },
accessTokenSecret,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ sub: userId },
refreshTokenSecret,
{ expiresIn: '7d' }
);
Input Validation and Sanitization prevents injection attacks:
// Validate claims before processing
function validateTokenClaims(token) {
const decoded = jwt.decode(token, { complete: true });
if (!decoded || !decoded.payload) {
throw new Error('Invalid token structure');
}
const claims = decoded.payload;
// Validate required claims
if (!claims.sub || !claims.exp) {
throw new Error('Missing required claims');
}
// Validate expiration
if (claims.exp < Math.floor(Date.now() / 1000)) {
throw new Error('Token expired');
}
return claims;
}
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |