CWE-125 in APIs
- CWE ID
- CWE-125
- Category
- Input Validation
- Severity
- HIGH
- Short Name
- OOB Read
What is CWE-125?
CWE-125, or Out-of-Bounds Read, occurs when an application reads data from outside the intended buffer boundaries. This weakness allows attackers to read sensitive memory contents that should not be accessible, potentially exposing passwords, cryptographic keys, or other confidential information. The vulnerability arises when an application calculates an incorrect buffer size or uses an invalid index to access memory.
In memory-managed languages like C and C++, this weakness is particularly dangerous because it can expose arbitrary memory contents. An attacker might leverage this to bypass security mechanisms, extract sensitive data, or even influence application behavior by reading uninitialized or corrupted memory regions.
CWE-125 in API Contexts
In API development, CWE-125 manifests in several specific ways. One common scenario involves array index validation failures. Consider an API endpoint that accepts pagination parameters:
app.get('/api/users', (req, res) => {
const page = parseInt(req.query.page) || 0;
const limit = parseInt(req.query.limit) || 10;
const users = await db.query('SELECT * FROM users LIMIT ? OFFSET ?', [limit, page * limit]);
res.json(users);
});If the database query returns fewer results than requested, the API might still attempt to access array indices that don't exist, especially when processing results in memory. This could expose uninitialized memory or previous request data.
Another API-specific manifestation occurs with JSON parsing. When an API parses incoming JSON requests, improper validation of array bounds can lead to out-of-bounds reads:
const data = JSON.parse(req.body);
const items = data.items || [];
for (let i = 0; i <= items.length; i++) { // <= should be <
processItem(items[i]); // items[items.length] is out of bounds
}Buffer over-reads also occur when APIs handle binary data or file uploads without proper size validation. An attacker could craft requests with manipulated content-length headers or oversized payloads to trigger out-of-bounds reads during processing.
Detection
Detecting CWE-125 requires both static analysis and dynamic testing. Static analysis tools can identify potential buffer boundary violations by examining code paths and data flow. Look for patterns like array access without bounds checking, pointer arithmetic without validation, or unsafe string operations.
Dynamic testing involves sending crafted requests to API endpoints to trigger potential out-of-bounds conditions. This includes testing with boundary values, negative indices, extremely large numbers, and unexpected data types. Tools like fuzzers can automatically generate these test cases.
middleBrick includes CWE-125 detection in its comprehensive API security scanning. The scanner tests for out-of-bounds conditions by sending boundary-testing requests to your API endpoints. For example, it will test pagination parameters with negative values, extremely large numbers, and non-numeric inputs to identify potential array index validation failures.
The scanner also examines your OpenAPI/Swagger specifications to identify parameters that should have bounds validation but lack proper constraints. If your spec defines a numeric parameter without minimum/maximum values, middleBrick flags this as a potential CWE-125 risk.
During scanning, middleBrick runs 12 parallel security checks including input validation testing that specifically targets boundary condition vulnerabilities. The scanner attempts to access array indices beyond valid ranges and monitors for error responses that might indicate memory access violations or information leakage.
Remediation
Fixing CWE-125 requires implementing proper bounds checking and input validation throughout your API code. Here are concrete remediation strategies with code examples:
1. Array Index Validation
function getSafeItem(array, index) {
if (!Array.isArray(array)) {
throw new Error('Expected array');
}
if (!Number.isInteger(index) || index < 0 || index >= array.length) {
throw new Error('Index out of bounds');
}
return array[index];
}
// Usage in API endpoint
app.get('/api/items/:index', (req, res) => {
const index = parseInt(req.params.index);
const item = getSafeItem(items, index);
res.json(item);
});2. Pagination with Bounds Checking
app.get('/api/resources', (req, res) => {
const page = Math.max(0, parseInt(req.query.page) || 0);
const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 10));
const offset = page * limit;
const resources = await db.query(
'SELECT * FROM resources LIMIT ? OFFSET ?',
[limit, offset]
);
if (resources.length < limit && page > 0) {
return res.status(404).json({
error: 'Page out of range'
});
}
res.json({
page,
limit,
total: resources.length,
data: resources
});
});3. JSON Array Validation
app.post('/api/process', (req, res) => {
const { items } = req.body;
if (!Array.isArray(items)) {
return res.status(400).json({
error: 'Expected array for items'
});
}
if (items.length > 1000) {
return res.status(400).json({
error: 'Items array too large'
});
}
const results = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (typeof item !== 'object' || item === null) {
return res.status(400).json({
error: `Invalid item at index ${i}`
});
}
results.push(processItem(item));
}
res.json(results);
});4. Buffer Size Validation for Binary Data
app.post('/api/upload', (req, res) => {
const maxFileSize = 1024 * 1024; // 1MB
if (req.headers['content-length'] > maxFileSize) {
return res.status(413).json({
error: 'File too large'
});
}
let buffer = Buffer.alloc(0);
req.on('data', chunk => {
if (buffer.length + chunk.length > maxFileSize) {
return res.status(413).json({
error: 'File too large'
});
}
buffer = Buffer.concat([buffer, chunk]);
});
req.on('end', () => {
// Process buffer safely
processBuffer(buffer);
res.json({ success: true });
});
});5. Type Safety and Input Sanitization
function safeParseInt(value, defaultValue = 0) {
const num = parseInt(value);
return Number.isNaN(num) ? defaultValue : num;
}
function validateBounds(value, min, max) {
const num = safeParseInt(value);
return Math.min(max, Math.max(min, num));
}
// Usage
app.get('/api/search', (req, res) => {
const page = validateBounds(req.query.page, 0, 1000);
const limit = validateBounds(req.query.limit, 1, 100);
// Safe to use without out-of-bounds risk
const results = await searchDatabase(page, limit);
res.json(results);
});