Crlf Injection in Restify with Mutual Tls
Crlf Injection in Restify with Mutual Tls
Crlf Injection occurs when user-controlled data is reflected in HTTP headers without sanitization, allowing an attacker to inject newline characters (CRLF: \r\n) to split headers and inject additional ones. In Restify, this commonly manifests via the res.setHeader() or response header manipulation APIs when concatenating or directly assigning values from request parameters, headers, or cookies.
Mutual Tls (mTLS) changes the threat surface by ensuring the client presents a valid certificate, but it does not automatically sanitize data. mTLS authenticates the client identity, which can lead developers to assume that all downstream data is trusted. This trust can result in insufficient input validation on authenticated channels. For example, an authenticated client might supply a username or an identifier that later gets reflected in a custom header such as x-user-display. If Restify sets this header without canonicalization, an authenticated mTLS client can inject \r\nContent-Security-Policy: none or \r\nSet-Cookie: session=hijacked.
Because mTLS terminates TLS at the server and presents client cert details to the application, logging and observability may include certificate fields (e.g., subject DN). If these fields are used directly in headers without validation, injection becomes feasible. Consider a handler that copies a certificate field into a response header:
const server = restify.createServer();
server.get('/profile', (req, res, next) => {
const subject = req.client.authorized ? req.client.cert.subject : 'unknown';
// Risky: subject may contain newlines if not validated
res.setHeader('X-Cert-Subject', subject);
res.send({ ok: true });
return next();
});
server.listen(8080);
An authenticated client with a malicious certificate containing \r\nX-Injected: 1 can cause the server to emit two headers, potentially enabling cache poisoning, HTTP response splitting, or header smuggling. Even when TLS is mutual, the application must treat all data derived from the request (including certificate attributes) as untrusted for header construction.
The impact of successful Crlf Injection in a mTLS context includes:
- HTTP response splitting, which can corrupt downstream caches or proxies.
- Header injection leading to unintended redirects or cookie modifications.
- Bypassing security controls that rely on header integrity, such as certain session fixation protections.
Remediation focuses on strict input validation and canonicalization. Do not rely on transport-layer authentication to enforce data safety. Validate and sanitize any data that may become part of HTTP headers, including certificate fields, query parameters, and JSON payloads. MiddleBrick scans can surface these header-injection risks in unauthenticated and mTLS scenarios by analyzing endpoint behavior and spec definitions.
Mutual Tls-Specific Remediation in Restify
Secure Restify handlers in mTLS environments by explicitly validating and sanitizing any data that flows into HTTP headers. Treat certificate-derived values as untrusted strings. Below are concrete code examples demonstrating safe practices.
Safe header setting with canonicalization: Strip or replace newline characters and enforce a strict allowlist for values placed in headers.
function safeHeaderValue(value) {
// Remove CR and LF characters; reject if any remain after stripping
const cleaned = value.replace(/[\r\n]+/g, '');
if (cleaned !== value) {
throw new Error('Invalid header value: contains newline characters');
}
return cleaned;
}
const server = restify.createServer();
server.get('/profile', (req, res, next) => {
const rawSubject = req.client.authorized ? req.client.cert.subject : 'unknown';
try {
const subject = safeHeaderValue(rawSubject);
res.setHeader('X-Cert-Subject', subject);
} catch (err) {
// Reject or sanitize further; do not propagate unsafe values
res.send(400, { error: 'invalid_client_data' });
return next();
}
res.send({ ok: true });
return next();
});
server.listen(8080);
Using a validation library for structured data: If you expect structured values (e.g., JSON in headers via custom encoding), parse and validate before assignment.
const safeHeaderValue = (val) => {
const cleaned = val.replace(/[\r\n]/g, '');
if (cleaned !== val) throw new Error('newline injection');
return cleaned;
};
server.get('/user', (req, res, next) => {
const encoded = req.query.display; // e.g., base64 or escaped string
if (!encoded) return res.send(400, { error: 'missing_display' });
let display;
try {
display = decodeURIComponent(encoded);
} catch (_) {
return res.send(400, { error: 'invalid_encoding' });
}
try {
res.setHeader('X-User-Display', safeHeaderValue(display));
} catch (err) {
return res.send(400, { error: 'invalid_header_value' });
}
res.send({ user: display });
return next();
});
Middleware approach to enforce header safety across routes: Centralize validation so that all handlers benefit from consistent rules.
const headerSanitizer = (req, res, next) => {
res.setHeader = (name, value) => {
const cleaned = String(value).replace(/[\r\n]+/g, '');
if (cleaned !== value) {
throw new Error(`Header injection prevented: ${name}`);
}
res._originalSetHeader(name, cleaned);
};
res._originalSetHeader = res.setHeader;
next();
};
server.pre(headerSanitizer);
server.get('/any', (req, res, next) => {
// All res.setHeader calls below are sanitized
res.setHeader('X-Safe', req.headers['x-safe'] || 'default');
res.send({ status: 'ok' });
return next();
});
These patterns ensure that even when mTLS provides client authentication, response headers remain well-formed and injection vectors are closed. For broader coverage, use the middleBrick CLI to scan endpoints and detect header-injection risks across your API surface.