Crlf Injection in Sails with Cockroachdb
Crlf Injection in Sails with Cockroachdb — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject a CRLF sequence (\r\n) into a header or query parameter, causing the application to misinterpret boundaries between headers and body or to inject additional headers. In Sails, this risk arises when user-controlled input is reflected in HTTP headers or used to construct queries without proper sanitization, and it is particularly dangerous when combined with Cockroachdb as the backend datastore.
Sails is a Node.js MVC framework that often uses Waterline ORM. When a controller passes unsanitized request parameters into a database query or into a response header, an attacker can inject line breaks and arbitrary header fields. For example, an idempotent API endpoint like /user/info?id=123 might include the id in a custom header or log entry. If the input contains %0D%0A or literal \r\n, Sails may forward these to the response layer before Cockroachdb processes the query, enabling header manipulation.
With Cockroachdb, which is PostgreSQL wire-protocol compatible, the injection does not alter SQL syntax directly, but it can change how the application interprets results or forwards data to downstream services. A crafted payload in a query parameter could allow an attacker to inject a header such as X-Data-Leak: true or to split responses, leading to HTTP response splitting. This can facilitate cross-user attacks, cache poisoning, or information disclosure when logs or monitoring tools reflect the manipulated headers. Because Cockroachdb returns structured data, the injected headers may not be visible in the database but can affect the surrounding application logic, such as authentication checks or content negotiation, if Sails passes unchecked input into its HTTP layer.
The risk is compounded when Sails applications use automatic model mapping or blueprint APIs that forward request parameters directly to database queries. An attacker could provide a malicious string like 123\r\nX-Injected: injected in a filter or sort parameter. If the framework or an intermediary library concatenates this into a header or log line before sending the response to Cockroachdb, the injected line break can terminate a header prematurely and append new content, violating the intended protocol structure.
Cockroachdb-Specific Remediation in Sails — concrete code fixes
Remediation focuses on input validation, strict header control, and safe query construction. Never trust request parameters that reach HTTP headers or are concatenated into values that influence response formatting. Use explicit allowlists and avoid reflecting user input into headers.
1. Validate and sanitize inputs before they reach headers or logs
Ensure that any data used in HTTP headers is stripped of CRLF characters. In Sails, you can create a policy or middleware to sanitize inputs globally or for specific routes.
// api/policies/sanitizeHeaders.js
module.exports.sanitizeHeaders = function(req, res, next) {
const sanitize = (str) => {
if (typeof str !== 'string') return str;
return str.replace(/[\r\n]+/g, '');
};
req.allParams().forEach((key) => {
const val = req.param(key);
if (typeof val === 'string') {
req.options[key] = sanitize(val);
}
});
return next();
};
Apply this policy to controllers that interact with Cockroachdb to ensure that injected line breaks cannot reach headers or logs.
2. Use parameterized queries with Waterline ORM
Always use built-in Waterline methods that separate SQL logic from data. Avoid string concatenation when building queries that involve user input.
// api/controllers/UserController.js
module.exports.info = async function(req, res) {
const id = req.param('id');
// Safe: Waterline parameterization prevents injection into SQL sent to Cockroachdb
const user = await User.findOne({ id: id });
if (!user) return res.notFound();
// Ensure no user-controlled data is placed into headers
return res.ok(user);
};
If you must set custom headers, use hardcoded values or values from a trusted source, never directly from req.params or req.body.
3. Explicitly set headers and avoid reflection
When constructing responses, set headers explicitly and avoid echoing user input. If you must include dynamic values, encode or restrict them strictly.
// api/responses/withHeaders.js
module.exports.withHeaders = function(userData, res) {
// Good: header values are controlled, not derived from user input
res.set('X-Content-Type', 'application/json');
res.set('X-Request-ID', 'fixed-value-or-uuid');
return res.ok(userData);
};
4. Reject or encode inputs containing CRLF in APIs
For endpoints that accept strings that may later be used in logs or headers, enforce strict validation.
// api/validators/noCrlf.js
module.exports.noCrlf = function(input) {
if (input && input.toString().includes('\r') || input.toString().includes('\n')) {
throw new Error('Invalid input: contains line break characters');
}
return input;
};
// Usage in a controller before any header construction
const clean = require('../../../validators/noCrlf');
module.exports.create = async function(req, res) {
const email = clean(req.body.email);
const record = await User.create({ email }).fetch();
return res.created(record);
};
5. Configure security headers in Sails hooks
Use Sails hooks to enforce defensive headers and reduce the impact of any accidental reflection.
// config/hooks.js
module.exports.hooks = {
securityHeaders: function(req, res, next) {
res.set('Content-Security-Policy', "default-src 'self'");
res.set('X-Frame-Options', 'DENY');
res.set('Strict-Transport-Security', 'max-age=63072000');
return next();
}
};
By combining input sanitization, strict query practices, and controlled header usage, you mitigate Crlf Injection risks in Sails applications backed by Cockroachdb. These steps ensure that user-supplied data never corrupts the HTTP layer or undermines the integrity of responses.