Data Exposure in Express
How Data Exposure Manifests in Express
Data exposure in Express applications occurs when sensitive information is inadvertently returned to clients through API responses. This manifests in several Express-specific ways:
- Debug information in error responses - When Express encounters an error, it may include stack traces or internal server details in the response body. The default error handler in development mode sends full stack traces, exposing file paths, line numbers, and potentially database queries.
- Verbose logging middleware - Middleware like Morgan or custom loggers may capture and expose sensitive request data (headers, bodies, query parameters) in logs that are accessible to unauthorized users.
- Unintended property exposure - When using res.json() or res.send() with objects, Express serializes all enumerable properties. If objects contain sensitive fields (passwords, API keys, internal IDs), these get exposed without explicit filtering.
- Header information leakage - Express's default headers (X-Powered-By: Express) and custom error headers can reveal implementation details that aid attackers.
- Database error propagation - When database operations fail, Express may return raw error objects containing SQL queries, connection strings, or database schema information.
Consider this vulnerable Express pattern:
app.get('/users/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
res.json(user); // Exposes password hash, internal IDs
} catch (err) {
res.status(500).json({ error: err.message, stack: err.stack }); // Exposes stack trace
}
});
This code exposes the user's password hash and internal database IDs to any authenticated user who can guess IDs, plus detailed stack traces on errors.
Express-Specific Detection
Detecting data exposure in Express requires both manual code review and automated scanning. Here's how to identify these vulnerabilities:
Manual Code Review Patterns
- Search for res.json() and res.send() calls that serialize entire objects without property filtering
- Look for error handling that sends err.message or err.stack directly to clients
- Check for verbose logging middleware that logs request bodies or headers
- Review database query error handling for raw error exposure
middleBrick Scanning
middleBrick's Data Exposure check specifically targets Express applications by:
- Analyzing API responses for sensitive data patterns (passwords, tokens, keys, PII)
- Testing error endpoints to see if stack traces are exposed
- Checking response headers for verbose information
- Scanning OpenAPI specs for endpoints that might return sensitive data
The scanner runs 12 parallel security checks including Data Exposure, testing your unauthenticated attack surface in 5-15 seconds. For Express apps, it specifically looks for:
{
"data_exposure": {
"high_risk": ["password", "token", "api_key"],
"medium_risk": ["email", "phone", "address"],
"headers_exposed": ["x-powered-by", "server"],
"error_details": ["stack_traces", "internal_errors"]
}
}
middleBrick's black-box scanning tests your running Express API without requiring credentials or code access, making it ideal for detecting data exposure in production environments.
Express-Specific Remediation
Fixing data exposure in Express requires both code changes and configuration adjustments. Here are Express-specific solutions:
1. Property Filtering with Select/Projection
Instead of serializing entire objects, explicitly select which properties to return:
app.get('/users/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id, 'name email role');
res.json(user); // Only returns specified fields
} catch (err) {
res.status(404).json({ error: 'User not found' });
}
});
2. Error Handling Middleware
Replace default error handling with Express middleware that sanitizes responses:
// Centralized error handler
app.use((err, req, res, next) => {
console.error(err); // Log full error internally
const errorResponse = {
message: process.env.NODE_ENV === 'development'
? err.message
: 'Internal server error',
statusCode: err.statusCode || 500
};
res.status(errorResponse.statusCode).json(errorResponse);
});
// For async route handlers
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) throw new Error('User not found');
res.json({ name: user.name, email: user.email });
}));
3. Response Sanitization Middleware
Create middleware to sanitize responses before sending:
const sanitizeResponse = (obj, allowedFields) => {
if (Array.isArray(obj)) {
return obj.map(item => sanitizeResponse(item, allowedFields));
}
if (typeof obj === 'object' && obj !== null) {
return Object.keys(obj)
.filter(key => allowedFields.includes(key))
.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {});
}
return obj;
};
app.use((req, res, next) => {
const oldSend = res.send;
res.send = function(data) {
if (typeof data === 'object') {
const sanitized = sanitizeResponse(data, ['id', 'name', 'email']);
oldSend.call(this, sanitized);
} else {
oldSend.call(this, data);
}
};
next();
});
4. Security Headers Middleware
Remove verbose headers and add security headers:
const helmet = require('helmet');
app.use(helmet({
hidePoweredBy: { setTo: 'PHP 4.2.0' }, // Obfuscate technology
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:']
}
}
}));
5. Logging Configuration
Configure logging to avoid exposing sensitive data:
const morgan = require('morgan');
const fs = require('fs');
// Custom token that excludes sensitive headers
morgan.token('body', (req, res) => {
const { password, token, api_key, ...safeBody } = req.body;
return JSON.stringify(safeBody);
});
// Log to file without sensitive data
app.use(morgan(':method :url :status :response-time ms - :body', {
stream: fs.createWriteStream('./access.log', { flags: 'a' })
}));
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |