CWE-400 in APIs
- CWE ID
- CWE-400
- Category
- Resource Consumption
- Severity
- HIGH
- Short Name
- DoS
What is Cwe 400?
Cwe 400: Uncontrolled Resource Consumption occurs when a system fails to properly control the allocation and use of resources such as memory, CPU, disk space, or network connections. This weakness allows attackers to exhaust available resources, causing denial of service or severe performance degradation.
The official CWE description states that this weakness exists when "the software does not properly control the allocation and maintenance of a limited resource, thereby enabling an attacker to influence the amount of resources consumed, eventually leading to the exhaustion of available resources."
In practical terms, this means an attacker can send requests that cause the application to consume excessive memory, CPU cycles, or other resources without proper limits or cleanup mechanisms. Common manifestations include memory leaks, unbounded data structures, and infinite loops triggered by malicious input.
Cwe 400 in API Contexts
APIs are particularly vulnerable to resource consumption attacks because they're designed to handle external requests automatically. Several API-specific scenarios commonly lead to Cwe 400 vulnerabilities:
- Unbounded result sets: APIs that return all database records without pagination can exhaust memory when clients request large datasets
- Recursive endpoint calls: Endpoints that can trigger themselves through references create infinite loops
- Large file uploads: APIs accepting file uploads without size limits can consume all available disk space
- Expensive operations without limits: Endpoints performing complex calculations or data processing without timeout controls
- Connection pooling exhaustion: APIs that don't properly close database connections or external service connections
A real-world example occurred in 2021 when a popular e-commerce API was found vulnerable to resource exhaustion through its product search endpoint. The endpoint allowed unlimited recursive category traversal, enabling attackers to trigger exponential API calls that consumed 100% of available CPU resources within minutes.
Detection
Detecting Cwe 400 requires both static analysis and dynamic testing approaches. Here's how to identify this weakness:
- Code review for resource allocation patterns: Look for loops that add to collections without size checks, recursive functions without depth limits, and unbounded database queries
- Performance monitoring: Monitor API response times, memory usage, and CPU utilization under load to identify abnormal resource consumption patterns
- Fuzz testing: Send malformed or exceptionally large inputs to test how the API handles resource-intensive operations
- Automated scanning: Use specialized tools that can detect resource consumption vulnerabilities
middleBrick scans for Cwe 400 by testing API endpoints with resource-intensive inputs and monitoring system behavior. The scanner identifies endpoints that fail to enforce size limits, don't implement proper timeouts, or allow recursive operations without depth controls. For example, middleBrick tests pagination endpoints by requesting increasingly large page sizes to see if the API enforces reasonable limits.
The scanner also checks for common API misconfigurations that lead to resource exhaustion, such as missing Content-Length headers on file uploads, unbounded JSON payloads, and endpoints that perform synchronous external API calls without timeout controls.
Remediation
Fixing Cwe 400 requires implementing proper resource controls and limits throughout your API. Here are code-level solutions with examples:
1. Implement Pagination and Result Limits
// Vulnerable: Returns all records without limits
app.get('/api/users', async (req, res) => {
const users = await User.find();
res.json(users);
});
// Fixed: Implements pagination with maximum limits
app.get('/api/users', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = Math.min(parseInt(req.query.limit) || 20, 100); // Max 100 records
const skip = (page - 1) * limit;
const [users, totalCount] = await Promise.all([
User.find().skip(skip).limit(limit),
User.countDocuments()
]);
res.json({
data: users,
pagination: {
page,
limit,
total: totalCount,
pages: Math.ceil(totalCount / limit)
}
});
});
2. Add Size Limits to File Uploads
// Vulnerable: No file size limits
app.post('/api/upload', upload.single('file'), (req, res) => {
// Process file...
});
// Fixed: Enforces maximum file size
const upload = multer({
limits: {
fileSize: 10 * 1024 * 1024, // 10MB maximum
files: 1
},
fileFilter: (req, file, cb) => {
if (file.mimetype !== 'application/pdf') {
return cb(new Error('Only PDF files allowed'));
}
cb(null, true);
}
});
3. Implement Timeout Controls
// Vulnerable: No timeout on external API calls
app.get('/api/external-data', async (req, res) => {
const response = await axios.get('https://external.service/data');
res.json(response.data);
});
// Fixed: Implements timeout and circuit breaker
app.get('/api/external-data', async (req, res, next) => {
try {
const response = await axios.get('https://external.service/data', {
timeout: 5000, // 5 second timeout
maxRetries: 3,
retryDelay: 1000
});
res.json(response.data);
} catch (error) {
if (error.code === 'ECONNABORTED') {
return res.status(503).json({
error: 'External service timeout'
});
}
next(error);
}
});
4. Prevent Recursive Endpoint Calls
// Vulnerable: Allows recursive category traversal
app.get('/api/categories/:id', async (req, res) => {
const category = await Category.findById(req.params.id);
const subcategories = await Category.find({ parent: category._id });
// This can create infinite recursion
const fullTree = await Promise.all(
subcategories.map(async (subcat) => {
return {
...subcat.toJSON(),
children: await getChildren(subcat._id) // Recursive call
};
})
);
res.json(fullTree);
});
// Fixed: Implements depth limit and cycle detection
async function getCategoryTree(id, depth = 0, maxDepth = 5, visited = new Set()) {
if (depth > maxDepth) {
throw new Error('Maximum recursion depth exceeded');
}
if (visited.has(id)) {
throw new Error('Circular reference detected');
}
visited.add(id);
const category = await Category.findById(id);
if (!category) {
return null;
}
const subcategories = await Category.find({ parent: id });
const children = await Promise.all(
subcategories.map(subcat =>
getCategoryTree(subcat._id, depth + 1, maxDepth, new Set(visited))
)
);
return {
...category.toJSON(),
children: children.filter(child => child !== null)
};
}
5. Implement Rate Limiting
const rateLimit = require('express-rate-limit');
// Vulnerable: No rate limiting
app.get('/api/data', (req, res) => {
// Process request...
});
// Fixed: Implements rate limiting
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
app.use('/api/', apiLimiter);
// More specific limits for expensive endpoints
const expensiveEndpointLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 10, // 10 requests per hour
skip: (req) => req.user?.role === 'admin' // Skip for admins
});
app.get('/api/expensive-operation', expensiveEndpointLimiter, async (req, res) => {
// Process expensive operation...
});