Prototype Pollution with Bearer Tokens
How Prototype Pollution Manifests in Bearer Tokens
Prototype pollution in Bearer Tokens occurs when malicious data in the Authorization header or token payload can manipulate JavaScript object prototypes, leading to unexpected behavior in downstream processing. This vulnerability is particularly dangerous in Node.js applications that parse Bearer tokens and use the resulting data without proper sanitization.
The most common attack vector involves crafting a token with specially formatted payload that, when parsed by the server, modifies Object.prototype. For example, an attacker might send a JWT with a payload containing __proto__ or constructor properties that overwrite built-in methods:
const maliciousToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJzdXBlclByb3RvIjoie1widXJsXCI6XCJodHRwczovL2V4YW1wbGUuY29tP2E9J2InXCJ9In0=.signature'When this token is decoded and the payload is merged into an object, the __proto__ property can add a url field to Object.prototype, affecting all objects:
// Vulnerable code pattern in Bearer Tokens processing
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.decode(token); // No validation, just decode
const userData = { ...decoded }; // Prototype pollution possible
// Now every object has url property
console.log({}.url); // Outputs: https://example.com?a='b'
Another manifestation occurs in API gateway configurations where Bearer tokens are used to determine routing or feature access. An attacker might exploit prototype pollution to bypass authorization checks:
// Vulnerable routing logic
const routeConfig = require('./routes.json');
const tokenData = jwt.decode(bearerToken);
// Prototype pollution: __proto__ adds isAdmin to all objects
if (tokenData.isAdmin) {
return routeConfig.adminRoutes;
} else {
return routeConfig.publicRoutes;
}
The pollution can also affect caching mechanisms. If a Bearer token is used as a cache key and contains prototype pollution, it can corrupt the entire cache:
// Vulnerable caching pattern
const cache = {};
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.decode(token);
// Prototype pollution corrupts cache structure
cache[token] = decoded;
// Now cache has unexpected properties
console.log(cache.__proto__); // Modified by token
Bearer Tokens-Specific Detection
Detecting prototype pollution in Bearer Tokens requires both static analysis and runtime scanning. middleBrick's black-box scanning approach is particularly effective because it tests the unauthenticated attack surface without requiring credentials.
middleBrick scans for prototype pollution by sending specially crafted tokens that attempt to modify Object.prototype. The scanner tests for common patterns like __proto__, constructor, and prototype properties in the token payload. Here's what the detection process looks like:
// Example of what middleBrick tests for
const testPayloads = [
{ '__proto__': { 'isAdmin': true } },
{ 'constructor': { 'prototype': { 'isAdmin': true } } },
{ 'prototype': { 'isAdmin': true } }
];
// The scanner sends these as JWT payloads and observes server behavior
// It checks if the properties persist in subsequent requests or affect
// other endpoints that shouldn't be accessible
Manual detection involves testing with crafted tokens and observing object behavior. Use tools like jwt.io to create tokens with malicious payloads:
// Create a malicious token
const jwt = require('jsonwebtoken');
const payload = {
'user': 'admin',
'__proto__': {
'isAdmin': true,
'dangerousMethod': function() { return 'HACKED'; }
}
};
const token = jwt.sign(payload, 'secret');
console.log(token);
Runtime detection can be implemented by monitoring object prototypes during token processing:
// Detection middleware
function detectPrototypePollution(req, res, next) {
const originalProto = Object.prototype.toString;
// Test if prototype has been modified
if (Object.prototype.toString !== originalProto) {
console.warn('Prototype pollution detected!');
// Log the difference
console.log('Modified prototype:', Object.prototype.toString);
}
next();
}
middleBrick's API security scanning includes specific checks for Bearer token vulnerabilities, testing for:
| Check Type | What It Tests | Impact |
|---|---|---|
| Prototype Property Injection | __proto__, constructor, prototype fields | Object corruption |
| Property Escalation | Privilege escalation via token properties | Authorization bypass |
| Cache Poisoning | Prototype pollution affecting caching | Denial of service |
The scanner provides severity ratings and remediation guidance specific to Bearer token implementations, helping developers understand the exact impact on their authentication flow.
Bearer Tokens-Specific Remediation
Remediating prototype pollution in Bearer token implementations requires a defense-in-depth approach. The most effective strategy combines input validation, secure parsing, and proper object handling.
First, validate and sanitize JWT payloads before processing. Use libraries that provide built-in protection against prototype pollution:
// Secure JWT verification with validation
const jwt = require('jsonwebtoken');
function verifyToken(token) {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Sanitize payload: remove dangerous properties
const safePayload = Object.entries(decoded).reduce((acc, [key, value]) => {
if (!['__proto__', 'constructor', 'prototype'].includes(key)) {
acc[key] = value;
}
return acc;
}, {});
return safePayload;
} catch (error) {
throw new Error('Invalid token');
}
}
Implement strict object creation patterns to prevent prototype pollution:
// Use Object.create(null) for clean objects
function createSecureContext(token) {
const decoded = jwt.decode(token);
// Create object without prototype chain
const context = Object.create(null);
// Only allow whitelisted properties
const allowedProperties = ['userId', 'role', 'permissions'];
for (const [key, value] of Object.entries(decoded)) {
if (allowedProperties.includes(key)) {
context[key] = value;
}
}
return context;
}
Configure your JWT library to use secure parsing options. Many modern libraries have built-in protections:
// Using jsonwebtoken with secure options
const jwt = require('jsonwebtoken');
function secureVerify(token) {
// Verify with options that prevent prototype pollution
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
complete: true
});
// Additional validation
if (typeof decoded.payload !== 'object') {
throw new Error('Invalid payload type');
}
return decoded.payload;
}
For applications using Bearer tokens with complex payloads, implement a schema validation layer:
// Using Joi for schema validation
const Joi = require('joi');
const tokenSchema = Joi.object({
userId: Joi.string().required(),
role: Joi.string().valid('user', 'admin', 'moderator').default('user'),
permissions: Joi.array().items(Joi.string()).default([])
}).unknown(false); // Reject unknown properties
function validateTokenPayload(payload) {
const { error, value } = tokenSchema.validate(payload);
if (error) {
throw new Error(`Invalid token payload: ${error.message}`);
}
return value;
}
middleBrick's remediation guidance includes specific recommendations for Bearer token implementations, such as:
- Using Object.create(null) for token-derived objects
- Implementing strict property whitelisting
- Configuring JWT libraries with secure parsing options
- Adding runtime prototype monitoring
- Regular security scanning of authentication endpoints
The Pro plan's continuous monitoring feature can automatically scan your Bearer token endpoints on a schedule, alerting you to any prototype pollution vulnerabilities that emerge from code changes or dependency updates.