Path Traversal with Api Keys
How Path Traversal Manifests in Api Keys
Path traversal vulnerabilities in API keys contexts occur when untrusted input is used to construct file paths or access resources without proper validation. In API key implementations, this often manifests through configuration files, key storage mechanisms, or dynamic path construction based on key metadata.
Consider an API key management system that stores keys in a directory structure based on user IDs. An attacker might manipulate their user ID to traverse directories and access other users' keys:
// Vulnerable pattern - no path validation
const userId = req.query.userId; // User supplies: ../../../admin
const keyPath = `/keys/${userId}/apikey.json`;
const keyData = fs.readFileSync(keyPath);
This code allows path traversal through the userId parameter, potentially exposing other users' API keys stored in the filesystem.
Another common pattern involves API key validation against configuration files:
// Vulnerable - direct path concatenation
function validateKey(key) {
const keyFile = `/config/keys/${key}.json`;
const config = fs.readFileSync(keyFile);
return JSON.parse(config);
}
If an attacker can control the key parameter, they might access arbitrary files like /config/keys/../../../etc/passwd.
Database-driven API key systems aren't immune either. When key metadata is used to construct file paths for rate limiting or audit logs:
// Vulnerable - metadata-based path traversal
async function logApiUsage(apiKey, metadata) {
const userId = metadata.userId; // Could contain ../
const logPath = `/logs/${userId}/${apiKey}.log`;
await fs.appendFile(logPath, generateLogEntry());
}
The severity of API key path traversal is high because it can lead to complete account takeover, data exfiltration, or service disruption. Attackers can read other users' keys, modify key permissions, or delete keys entirely.
Api Keys-Specific Detection
Detecting path traversal in API key systems requires examining both code patterns and runtime behavior. Static analysis should look for:
- Direct concatenation of user input with path strings
- Usage of
path.join(),path.resolve(), or similar functions without validation - Dynamic file path construction based on API key metadata
- Permission checks that rely on path structure rather than proper authorization
Runtime detection focuses on identifying suspicious file access patterns:
// Detection pattern - monitoring file access
const fs = require('fs');
const originalReadFile = fs.readFile;
fs.readFile = function(path, ...args) {
if (path.includes('..') || path.includes('~') || path.startsWith('/')) {
console.warn(`Suspicious path access: ${path}`);
// Log and alert for security review
}
return originalReadFile.apply(this, [path, ...args]);
};
Automated scanning tools like middleBrick can detect these vulnerabilities by testing for path traversal attempts across all API endpoints. The scanner attempts common traversal patterns:
// middleBrick scanning patterns
const traversalPatterns = [
'../', '../..', '../../', '../../..',
'%2e%2e%2f', '%2e%2e/', // URL encoded
'..\', '..\\' // Windows paths
];
middleBrick's black-box scanning approach tests these patterns against your API endpoints without requiring access to source code. The scanner specifically looks for API key endpoints, configuration endpoints, and any paths that might construct file paths dynamically.
Dynamic analysis should also monitor for:
- Unexpected file system access patterns
- Access to files outside expected directories
- Permission elevation through path manipulation
- Cross-tenant data access attempts
Integration with CI/CD pipelines using middleBrick's GitHub Action can automatically scan for these issues before deployment:
# .github/workflows/security.yml
name: API Security Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run middleBrick Scan
run: middlebrick scan https://api.yourdomain.com --fail-below B
Api Keys-Specific Remediation
Remediating path traversal in API key systems requires defense-in-depth approaches. The most fundamental fix is input validation and sanitization:
// Secure pattern - strict path validation
function validateApiKeyPath(inputPath, baseDir) {
// Remove any path traversal attempts
const sanitized = inputPath.replace(/(/|\)(?:\.\.)|(\x2e\x2e\x2f)/g, '');
// Resolve to absolute path and verify it's within base directory
const resolvedPath = path.resolve(baseDir, sanitized);
if (!resolvedPath.startsWith(baseDir)) {
throw new Error('Path traversal attempt detected');
}
return resolvedPath;
}
// Usage in API key validation
app.get('/api/keys/:key', (req, res) => {
try {
const key = req.params.key;
const safePath = validateApiKeyPath(key, '/keys');
const keyData = fs.readFileSync(safePath);
res.json(JSON.parse(keyData));
} catch (err) {
res.status(400).json({ error: 'Invalid key or path traversal attempt' });
}
});
Using Node.js's path module with proper validation provides another layer of security:
const path = require('path');
function safeFilePath(userInput, baseDir) {
const normalized = path.normalize(userInput);
const resolved = path.resolve(baseDir, normalized);
// Ensure the resolved path is within the base directory
if (!resolved.startsWith(baseDir + path.sep)) {
throw new Error('Invalid path');
}
return resolved;
}
For database-driven API key systems, avoid filesystem paths entirely:
// Secure pattern - no filesystem paths
class ApiKeyStore {
constructor(db) {
this.db = db;
}
async getApiKey(keyId) {
// Direct database lookup, no path construction
const result = await this.db.query(
'SELECT * FROM api_keys WHERE id = $1 AND active = true',
[keyId]
);
return result.rows[0];
}
async validateKey(keyString) {
// Hash comparison, no file access
const keyHash = crypto.createHash('sha256')
.update(keyString)
.digest('hex');
const result = await this.db.query(
'SELECT * FROM api_keys WHERE key_hash = $1 AND active = true',
[keyHash]
);
return result.rows[0];
}
}
Implement proper access controls using middleware:
function apiKeyAuthorization(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
try {
const keyData = validateApiKey(apiKey);
// Check if user is authorized for requested resource
if (!hasPermission(keyData.userId, req.path)) {
return res.status(403).json({ error: 'Access denied' });
}
req.apiKeyData = keyData;
next();
} catch (err) {
res.status(401).json({ error: 'Invalid API key' });
}
}
Rate limiting and monitoring should be implemented to detect suspicious patterns:
const rateLimit = require('express-rate-limit');
const traversalDetector = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // Limit each IP to 10 requests
skipSuccessfulRequests: false,
message: 'Suspicious activity detected',
keyGenerator: (req) => {
// Generate key based on suspicious patterns
const suspicious = req.path.includes('..') || req.path.includes('~');
return suspicious ? req.ip : req.ip + req.path;
}
});
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |
Frequently Asked Questions
How can I test if my API key system is vulnerable to path traversal?
../ sequences, URL-encoded equivalents, and Windows-style backslashes. Look for any endpoint that accepts user input and uses it to construct file paths or database queries.