Crlf Injection in Restify with Firestore
Crlf Injection in Restify with Firestore — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when untrusted data is placed into HTTP headers without proper sanitization, allowing an attacker to inject carriage return (\r) and line feed (\n) sequences. In a Restify service that integrates with Google Firestore, this typically arises when user-supplied input such as a document ID, query parameter, or request header is used to construct Firestore document references or is reflected into HTTP response headers. Because Restify allows custom header manipulation and Firestore document paths can include user-controlled strings, concatenating raw input into header values or document paths can enable an attacker to break out of the intended header or path structure.
For example, if a Restify endpoint builds a Firestore document path using user input and then sets a custom header with that value, unsanitized newlines can inject additional headers or even split the response, leading to response splitting or header smuggling. Consider a route like /users/:userId where userId is used both to fetch a Firestore document and to set a header such as X-User-Id. If an attacker sends a userId containing \r\nSet-Cookie: injected=value, Restify may append that crafted value to the response headers, potentially injecting a new cookie or overriding existing ones. Firestore itself does not execute the injected content, but the downstream HTTP layer (the Restify server) processes the malicious header, which can lead to cache poisoning, session fixation, or other header-based attacks.
Even when Firestore data is used indirectly—such as using a document field value in a header—the risk persists. If a Firestore document contains a user-controlled field that is later assigned to a response header in Restify without validation, an attacker who can write to that Firestore document (for instance, through insufficient client-side security rules or a compromised admin service account) can seed the injection payload. In this scenario, the vulnerability is not in Firestore’s query or write APIs, but in how the application uses Firestore data within HTTP message construction. Because the scan tests unauthenticated endpoints and header handling, middleBrick can detect such header injection vectors by sending crafted inputs containing newline sequences and inspecting whether injected headers appear in the response.
Another angle involves document ID or query parameter abuse. If a Restify route accepts a document ID and directly uses it to form a Firestore get or update operation, and also echoes that ID in headers or the response body, newline characters can split the protocol stream. For instance, setting a header like X-Document-Path: projects/{project}/databases/{database}/documents/col/{docId} with a docId containing \r\nContent-Length: 0 can confuse intermediate proxies or cause parsing anomalies. While Firestore will treat the document ID as a literal string (including backslashes and newlines as part of the ID), the surrounding HTTP layer may interpret the injected sequences, creating a discrepancy between the intended and actual message framing. middleBrick’s LLM/AI Security checks include active prompt injection probes and output scanning; in the context of API security, this methodology extends to detecting whether crafted newline sequences cause unexpected behavior in header parsing or response splitting, even when Firestore handles only the data layer.
Because Restify is a Node.js HTTP server framework, developers often rely on its built-in request parsing and header normalization. However, if user input is used to build Firestore document references or headers without canonicalization, the framework’s default behavior may not neutralize \r or \n. The Firestore client libraries treat document paths as opaque strings, so they do not block newline characters. This means the responsibility falls on the API layer to sanitize inputs before they are used in HTTP message construction. The combination of Restify’s flexible header handling and Firestore’s permissive document path strings creates a surface where Crlf Injection can be introduced if input validation is omitted.
Firestore-Specific Remediation in Restify — concrete code fixes
To remediate Crlf Injection in a Restify service using Firestore, ensure that any user-controlled data placed into HTTP headers or used to construct Firestore document paths is strictly validated and sanitized. The safest approach is to avoid inserting raw user input into headers entirely. If headers must reflect Firestore document identifiers or fields, normalize and restrict the content to safe characters.
Below are concrete Restify code examples that demonstrate insecure patterns and their secure counterparts.
Insecure example — using raw user input in a header:
const restify = require('restify');
const {Firestore} = require('@google-cloud/firestore');
const db = new Firestore();
const server = restify.createServer();
server.get('/users/:userId', async (req, res, next) => {
const userId = req.params.userId;
const docRef = db.doc(`users/${userId}`);
const doc = await docRef.get();
if (!doc.exists) return next(new restify.NotFoundError('User not found'));
// Insecure: directly using userId in a header
res.setHeader('X-User-Id', userId);
res.send(doc.data());
return next();
});
server.listen(8080);
In this example, an attacker can supply userId containing \r\nX-Injected: true, which may result in an additional header being appended. The fix is to sanitize userId before using it in a header.
Secure example — validating and sanitizing input:
const sanitizeHeaderValue = (value) => {
if (typeof value !== 'string') return '';
// Remove CR and LF characters and reject control characters
return value.replace(/[\r\n]/g, '');
};
server.get('/users/:userId', async (req, res, next) => {
const userId = sanitizeHeaderValue(req.params.userId);
if (!userId) return next(new restify.BadRequestError('Invalid user identifier'));
const docRef = db.doc(`users/${userId}`);
const doc = await docRef.get();
if (!doc.exists) return next(new restify.NotFoundError('User not found'));
// Safe: sanitized value used in header
res.setHeader('X-User-Id', userId);
res.send(doc.data());
return next();
});
This sanitization removes \r and \n, preventing injection into the header stream. For Firestore document paths, ensure the identifier is restricted to an acceptable pattern (e.g., alphanumeric, safe URL characters) before building the path. Avoid concatenating raw input into Firestore document references if it can be avoided; consider mapping user input to predefined document IDs or using Firestore’s built-in key structures.
Secure Firestore document path construction:
const allowedUserIdPattern = /^[a-zA-Z0-9_.-]+$/;
const validateAndGetUserId = (input) => {
if (!allowedUserIdPattern.test(input)) {
throw new Error('Invalid user ID');
}
return input;
};
server.get('/users/:userId', async (req, res, next) => {
try {
const userId = validateAndGetUserId(req.params.userId);
const docRef = db.doc(`users/${userId}`);
const doc = await docRef.get();
if (!doc.exists) return next(new restify.NotFoundError('User not found'));
// Use a sanitized, non-header field if you need to echo user identity
res.setHeader('X-User-Safe', encodeURIComponent(userId));
res.send(doc.data());
} catch (err) {
return next(new restify.BadRequestError('Invalid request'));
}
return next();
});
By validating the document ID against a strict pattern and avoiding the use of raw input in headers, you eliminate the Crlf Injection risk while still integrating Firestore with Restify. middleBrick’s scans include checks for Crlf Injection and can surface these issues in unauthenticated scans, helping you identify problematic header usage and data flow between your API layer and Firestore.