Header Injection in Express with Cockroachdb
Header Injection in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability
Header Injection occurs when user-controlled data is reflected into HTTP response headers without validation or encoding. In an Express application that interacts with CockroachDB, this typically happens when request parameters or headers are used to construct SQL queries or when dynamic values derived from database rows are written back into headers. CockroachDB, while PostgreSQL-wire compatible, does not change how Express handles headers; the risk arises from Express-side logic that fails to sanitize data before it reaches the header layer.
For example, if an endpoint accepts a userId query parameter and uses it in a SQL statement that also retrieves a display_name from CockroachDB, an attacker could supply a crafted value such as ?userId=1%0D%0aX-Content-Type-Options:%20nosniff. If the application directly uses the response field in a header (e.g., res.set('X-Display-Name', row.display_name)) without validation, the CRLF sequence can inject additional headers. This can lead to HTTP response splitting, cache poisoning, or bypassing security headers intended by the application.
Another scenario involves using request headers like X-Forwarded-For or X-Client-Trace-Id in SQL queries for logging or row-level filtering, and then echoing those values into other headers in the response. Because CockroachDB commonly serves distributed applications where multiple services exchange requests, the presence of custom headers and dynamic query construction increases the chance that unchecked input enters both the database path and the response path.
Express middleware that modifies res.locals or attaches user-derived properties to the response object can inadvertently expose injection points if those properties are later used in header-setting logic. For instance, code that does res.set(row.headerKey, row.headerValue) where headerKey or headerValue originate from CockroachDB rows is vulnerable if those columns contain CRLF characters or unexpected control data.
Because middleBrick scans the unauthenticated attack surface, it can detect whether response headers reflect unchecked database values by analyzing endpoints that reference user-controlled input or dynamic schema metadata. This is especially relevant when OpenAPI specs describe header parameters that are not validated against expected formats, and runtime probes confirm injection possibilities without requiring authentication.
Cockroachdb-Specific Remediation in Express — concrete code fixes
Remediation focuses on strict input validation, output encoding, and safe handling of database-driven values before they reach HTTP headers. Below are concrete Express patterns with CockroachDB queries that prevent header injection.
1. Validate and sanitize header names and values
Never use raw database columns as header keys or values. If you must derive headers from CockroachDB, enforce strict allow-lists and strip or encode CRLF characters.
const express = require('express');
const { Pool } = require('pg');
const app = express();
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
app.get('/user-header', async (req, res) => {
const userId = req.query.userId;
if (!/^[a-f0-9-]+$/.test(userId)) {
return res.status(400).send('Invalid user ID');
}
const client = await pool.connect();
try {
const result = await client.query('SELECT display_name FROM users WHERE id = $1', [userId]);
if (result.rows.length === 0) {
return res.status(404).send('Not found');
}
const displayName = result.rows[0].display_name;
// Encode newlines to prevent header injection
const safeValue = displayName.replace(/\r\n|\r|\n/g, ' ').trim();
res.set('X-Display-Name', safeValue);
res.json({ display_name: displayName });
} finally {
client.release();
}
});
2. Use parameterized queries and avoid dynamic header construction
Ensure SQL statements use placeholders and that application logic does not concatenate headers from rows without sanitization.
app.get('/report', async (req, res) => {
const reportId = req.query.reportId;
// Validate reportId format before using
if (!/^[0-9]+$/.test(reportId)) {
return res.status(400).send('Invalid report ID');
}
const client = await pool.connect();
try {
const result = await client.query('SELECT title, generated_by FROM reports WHERE id = $1', [reportId]);
if (result.rows.length === 0) {
return res.status(404).send('Not found');
}
const { title, generated_by } = result.rows[0];
// Do not set headers directly from database fields
res.set('X-Report-Title', Buffer.from(title).toString('base64'));
res.set('X-Generated-By', Buffer.from(generated_by || 'system').toString('base64'));
res.json({ title, generated_by });
} finally {
client.release();
}
});
3. Enforce header allow-lists in Express middleware
Use middleware to validate outgoing headers when they are influenced by database content.
const allowedHeaders = new Set(['content-type', 'x-request-id', 'x-locale']);
app.use((req, res, next) => {
res.on('header', () => {
const headerNames = Object.keys(res.getHeaders());
for (const name of headerNames) {
if (!allowedHeaders.has(name.toLowerCase())) {
// Log or handle unexpected headers instead of sending them
console.warn(`Unexpected header injected: ${name}`);
}
}
});
next();
});
By combining input validation, safe encoding, and allow-lists, Express applications can safely use CockroachDB data without exposing header injection vectors. These practices align with the OWASP API Security Top 10 and help prevent HTTP response splitting and related attacks.