Out Of Bounds Read in Express with Api Keys
Out Of Bounds Read in Express with Api Keys — how this specific combination creates or exposes the vulnerability
An Out Of Bounds Read occurs when an application reads memory beyond the intended allocation. In Express.js, this typically arises through unchecked user input used to index arrays, buffers, or strings. When API keys are handled without proper validation, an attacker can manipulate the key or related parameters to trigger such reads.
Consider an Express route that retrieves user configuration based on an API key index. If the index is parsed from request input and used directly to access an array, an out-of-bounds index can cause the runtime to read adjacent memory. For example:
const configs = [
{ key: 'abc123', tier: 'free' },
{ key: 'def456', tier: 'pro' }
];
app.get('/config', (req, res) => {
const idx = parseInt(req.query.index, 10);
// Potential out-of-bounds read if idx is negative or >= configs.length
const config = configs[idx];
res.json(config);
});
If an attacker provides index=-1 or a large positive number, the V8 engine may return an unexpected object or undefined, leading to information disclosure or instability. The API key itself may be embedded in logs, error messages, or responses when bounds violations cause inconsistent handling.
Another scenario involves buffer handling. If an API key is used to derive a buffer offset, an off-by-one error can expose adjacent memory:
const token = req.headers['x-api-key'];
const buffer = Buffer.from(token || '');
const offset = parseInt(req.query.offset, 10);
// Reading beyond buffer length if offset is not validated
const byte = buffer.readUInt8(offset);
res.send({ byte });
Here, an unsanitized offset can read bytes outside the token’s buffer. Because the API key is part of the buffer content, this may leak key fragments or internal structures. The combination of direct API key usage and unchecked indexing creates a path for unintended memory access.
In practice, such vulnerabilities may not always cause crashes; they can quietly expose sensitive data during routine requests. MiddleBrick’s checks for Data Exposure and Input Validation would flag these patterns, especially when API keys flow through unchecked index or offset operations.
Api Keys-Specific Remediation in Express — concrete code fixes
Secure handling of API keys in Express requires strict input validation, bounded data structures, and avoiding direct memory-sensitive indexing. Below are concrete, safe patterns.
1. Validate and sanitize all index/offset inputs
Ensure numeric inputs are within expected ranges before using them for array or buffer access:
app.get('/config', (req, res) => {
const idx = parseInt(req.query.index, 10);
const configs = [
{ key: 'abc123', tier: 'free' },
{ key: 'def456', tier: 'pro' }
];
if (Number.isNaN(idx) || idx < 0 || idx >= configs.length) {
return res.status(400).json({ error: 'Invalid index' });
}
const config = configs[idx];
res.json(config);
});
2. Avoid using raw API keys as buffer offsets
Do not derive memory offsets directly from API key values. If you must use binary data, enforce strict length checks:
const token = req.headers['x-api-key'];
if (typeof token !== 'string' || token.length !== 32) {
return res.status(400).json({ error: 'Invalid API key format' });
}
const buffer = Buffer.from(token, 'hex');
const offset = parseInt(req.query.offset, 10);
if (Number.isNaN(offset) || offset < 0 || offset + 1 > buffer.length) {
return res.status(400).json({ error: 'Invalid offset' });
}
const byte = buffer.readUInt8(offset);
res.send({ byte });
3. Use constant-time comparisons for key validation
Prevent timing attacks when comparing API keys by using a constant-time utility:
const crypto = require('crypto');
const expected = 'abc123';
const provided = req.headers['x-api-key'];
const isValid = crypto.timingSafeEqual(
Buffer.from(provided || ''),
Buffer.from(expected)
);
if (!isValid) {
return res.status(401).json({ error: 'Unauthorized' });
}
4. Scope API keys to minimal data access
Store configurations in a map keyed by API key rather than exposing array indices:
const configMap = {
'abc123': { tier: 'free' },
'def456': { tier: 'pro' }
};
app.get('/config', (req, res) => {
const key = req.headers['x-api-key'];
if (!key || !configMap[key]) {
return res.status(401).json({ error: 'Unauthorized' });
}
res.json(configMap[key]);
});
These approaches mitigate out-of-bounds risks by eliminating unchecked indexing, validating bounds explicitly, and avoiding memory-sensitive operations tied to API keys. MiddleBrick’s checks for BOLA/IDOR and Input Validation help detect remaining weak patterns.