Prototype Pollution with Api Keys
How Prototype Pollution Manifests in Api Keys
Prototype pollution in API keys environments typically occurs when user-controlled data is used to manipulate JavaScript object prototypes, particularly in Node.js applications that handle API key validation or configuration. This vulnerability allows attackers to inject arbitrary properties into Object.prototype, affecting all objects in the application.
In API keys contexts, prototype pollution often manifests through insecure deserialization of API key metadata, improper handling of configuration objects, or vulnerable request parsing. When an API key is used as an object key in a configuration map, an attacker can exploit prototype pollution to override critical security properties.
Consider this vulnerable pattern in API key validation:
const apiKeys = {
'valid-key-123': { permissions: ['read'] }
};
// Vulnerable: user input used as object key without validation
const userInput = JSON.parse(req.body);
const keyData = apiKeys[userInput.key];
// If userInput.key = '__proto__', this pollutes Object.prototype
// Now all objects gain the 'permissions' property
A common attack vector involves API endpoints that accept JSON objects where keys are dynamically used as object properties. An attacker can send {"__proto__": {"admin": true}} to elevate privileges across the entire application.
Another manifestation occurs in API key generation systems that use object spread or merge operations:
function mergeKeys(target, source) {
for (let key in source) {
target[key] = source[key];
}
return target;
}
// Vulnerable: __proto__ key allows prototype pollution
const maliciousKey = { '__proto__': { 'admin': true } };
const validKey = { 'permissions': ['read'] };
mergeKeys(validKey, maliciousKey);
// Now all objects have admin: true
API key validation middleware often becomes vulnerable when it uses dynamic property access based on user input:
function validateApiKey(key) {
const keyData = apiKeys[key];
if (keyData && keyData.permissions.includes('admin')) {
return true;
}
return false;
}
// If keyData was polluted via prototype, admin check always passes
Api Keys-Specific Detection
Detecting prototype pollution in API keys systems requires both static analysis and dynamic testing. The most effective approach combines automated scanning with manual code review of API key handling logic.
Static analysis should focus on these patterns:
// Look for these dangerous patterns
const patterns = [
/Object\.assign\([^)]*,[^)]*\)/g,
/for \(let [^ ]+ in [^)]+\)/g,
/JSON\.parse\([^)]*\)/g,
/\.\.\\.([^)]+)/g
];
Dynamic testing involves sending specially crafted payloads to API endpoints that handle API keys. Test with these payloads:
// Test for prototype pollution
const testPayloads = [
{ '__proto__': { 'admin': true } },
{ 'constructor': { 'prototype': { 'admin': true } } },
{ 'toString': () => { return 'malicious'; } }
];
middleBrick's scanning engine specifically targets prototype pollution in API keys contexts through these checks:
| Check Type | Detection Method | Api Keys Relevance |
|---|---|---|
| Prototype Pollution | Active probing with __proto__ payloads | API key validation bypasses |
| Property Injection | Testing dynamic property assignment | Configuration overrides |
| Object Mutation | Monitoring object state changes | Global permission changes |
middleBrick scans API endpoints in 5-15 seconds, testing the unauthenticated attack surface without requiring credentials. For prototype pollution detection, it sends controlled payloads and analyzes responses for indicators of successful exploitation.
Code-level detection using ESLint rules:
// .eslintrc.js
module.exports = {
rules: {
'no-prototype-builtins': 'error',
'no-for-in': 'warn',
'prefer-object-spread': 'error'
}
};
Api Keys-Specific Remediation
Remediating prototype pollution in API keys systems requires a defense-in-depth approach. The primary strategies involve input validation, safe object manipulation, and secure API key handling patterns.
Input validation is the first line of defense. Always validate object keys before use:
function sanitizeObjectKey(key) {
// Block prototype pollution keys
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
if (dangerousKeys.includes(key)) {
throw new Error('Invalid object key');
}
// Validate key format for API keys
if (!/^[a-zA-Z0-9_-]{16,64}$/.test(key)) {
throw new Error('Invalid API key format');
}
return key;
}
// Use in API key validation
function validateApiKey(key) {
const sanitizedKey = sanitizeObjectKey(key);
return apiKeys[sanitizedKey];
}
For object merging operations, use safe alternatives to prevent prototype pollution:
function safeMerge(target, source) {
const result = { ...target };
for (const [key, value] of Object.entries(source)) {
if (key.startsWith('__') || key === 'constructor') {
continue; // Skip dangerous keys
}
result[key] = value;
}
return result;
}
// Modern alternative using Object.create(null)
function createSafeObject() {
return Object.create(null); // No prototype chain
}
const apiKeys = createSafeObject();
apiKeys['valid-key'] = { permissions: ['read'] };
API key validation middleware should use strict property access:
const apiKeys = new Map();
apiKeys.set('valid-key-123', { permissions: ['read'] });
function validateApiKey(key) {
if (!apiKeys.has(key)) {
return false;
}
const keyData = apiKeys.get(key);
// Use Object.hasOwn for safe property checking
if (Object.hasOwn(keyData, 'permissions') &&
keyData.permissions.includes('admin')) {
return true;
}
return false;
}
For JSON parsing, use secure options:
function parseSecureJson(jsonString) {
try {
const parsed = JSON.parse(jsonString, (key, value) => {
// Filter dangerous keys during parsing
if (key === '__proto__' || key === 'constructor') {
throw new Error('Prototype pollution detected');
}
return value;
});
return parsed;
} catch (error) {
console.error('JSON parsing failed:', error.message);
return null;
}
}
Frequently Asked Questions
How can I test if my API keys system is vulnerable to prototype pollution?
Use middleBrick's free scanner to test your API endpoints. It actively probes for prototype pollution by sending controlled payloads with __proto__ keys and analyzes responses for indicators of successful exploitation. You can also manually test by sending POST requests with JSON payloads containing {"__proto__": {"admin": true}} to your API endpoints and observing if permissions are elevated.