Crlf Injection in Loopback
How Crlf Injection Manifests in Loopback
CRLF injection in Loopback applications typically occurs through HTTP header manipulation, response splitting, or improper handling of user-controlled input that ends up in HTTP headers or responses. Loopback's flexible response handling and middleware architecture creates several attack vectors.
The most common manifestation is through HTTP header injection. Consider this typical Loopback controller method:
async downloadFile(@param.query.string file: string) {
const filePath = path.join(__dirname, '../files/', file);
await this.response.download(filePath);
}An attacker could supply a file parameter like ../../etc/passwd%0D%0ASet-Cookie:%20evil=true. When Loopback processes this path and sends the response, the injected CRLF sequences create additional headers or split the response.
Another vulnerability appears in Loopback's dynamic response generation. Loopback allows setting response headers through various methods:
async customResponse(@param.query.string message: string) {
this.response.set('X-Custom-Header', message);
this.response.send('OK');
}If message contains %0D%0A sequences, an attacker can inject arbitrary headers or even create response splitting attacks where the browser interprets the response as multiple HTTP responses.
Loopback's file serving capabilities also introduce CRLF injection risks. The built-in file serving middleware processes URLs without proper sanitization:
router.get('/files/*', async (ctx) => {
const filePath = path.join(__dirname, '../public', ctx.request.path);
await ctx.send(ctx.request.path);
});An attacker could craft URLs with encoded CRLF sequences to manipulate the response structure or inject headers through path traversal combined with CRLF injection.
Loopback's OpenAPI/Swagger integration adds another layer of complexity. The framework automatically generates API documentation and handles parameter binding. If a parameter bound to a header or URL component isn't properly validated, CRLF sequences can propagate through the entire request processing pipeline.
Cross-site scripting combined with CRLF injection becomes particularly dangerous in Loopback applications. Consider a controller that reflects user input in error messages:
async search(@param.query.string query: string) {
try {
const results = await this.model.find({where: {name: query}});
return results;
} catch (error) {
this.response.status(400).send(`Error: ${error.message}`);
}
}If error.message contains CRLF sequences, an attacker can inject HTTP headers or split responses, potentially leading to cache poisoning or cross-user attacks.
Loopback-Specific Detection
Detecting CRLF injection in Loopback requires understanding the framework's request processing pipeline and common injection points. The most effective approach combines static analysis with runtime scanning.
Static analysis should focus on controller methods that handle user input and set response headers. Look for patterns like:
const dangerousPatterns = [
/%0D%0A/i,
/\r\n/i,
/\n\r/i,
/\r/i,
/\n/i
];Search your Loopback codebase for these patterns in controller methods, middleware, and any code that processes HTTP headers or responses. Pay special attention to methods using response.set(), response.append(), or direct header manipulation.
middleBrick's black-box scanning approach is particularly effective for Loopback applications. The scanner tests unauthenticated endpoints by sending payloads containing CRLF sequences and monitors responses for header injection or response splitting. For Loopback applications, middleBrick specifically tests:
- Query parameters that might be reflected in headers
- URL path components that could contain encoded CRLF
- POST request bodies that might be processed without proper sanitization
- Header injection through custom response methods
- OpenAPI parameter binding that could propagate CRLF sequences
The scanner's 12 security checks include specific tests for authentication bypass through header manipulation, which is a common CRLF injection goal in Loopback applications.
Runtime detection can be implemented using Loopback's middleware system. Create a middleware that inspects all incoming requests:
export async function crlfDetectionMiddleware(
ctx: RequestContext,
next: () => Promise
) {
const crlfRegex = /%0D%0A|\r\n|\n\r|%0D|%0A|\r|\n/gi;
// Check query parameters
for (const [key, value] of Object.entries(ctx.request.query)) {
if (typeof value === 'string' && crlfRegex.test(value)) {
console.warn(`CRLF injection attempt in query parameter: ${key}`);
}
}
// Check request body
if (ctx.request.hasBody) {
const body = await ctx.request.body({}).value;
if (body && typeof body === 'string' && crlfRegex.test(body)) {
console.warn('Potential CRLF injection in request body');
}
}
// Check headers
for (const [key, value] of Object.entries(ctx.request.headers)) {
if (typeof value === 'string' && crlfRegex.test(value)) {
console.warn(`CRLF injection attempt in header: ${key}`);
}
}
await next();
} Register this middleware globally to catch CRLF injection attempts across all endpoints.
middleBrick's continuous monitoring feature (Pro plan) can automatically scan your Loopback APIs on a schedule, providing ongoing detection of CRLF vulnerabilities as your codebase evolves.
Loopback-Specific Remediation
Remediating CRLF injection in Loopback applications requires a defense-in-depth approach using the framework's built-in security features and proper input validation.
The most fundamental fix is input sanitization using Loopback's validation system. For query parameters and request bodies:
import {inject} from '@loopback/core';
import {param, get} from '@loopback/rest';
export class SecureController {
constructor(@inject('logger') private logger: Console) {}
@get('/secure-download')
async secureDownload(
@param.query.string('file', {
required: true,
schema: {
type: 'string',
pattern: '^[a-zA-Z0-9._-]+$'
}
}) file: string
) {
// Only alphanumeric, dot, underscore, hyphen allowed
const sanitizedFile = file.replace(/[^a-zA-Z0-9._-]/g, '');
const filePath = path.join(__dirname, '../files/', sanitizedFile);
if (!filePath.startsWith(path.join(__dirname, '../files/'))) {
throw new HttpErrors.BadRequest('Invalid file path');
}
return await this.response.download(filePath);
}
}This approach uses Loopback's parameter validation to enforce a strict pattern, then applies additional sanitization before using the input.
For response header manipulation, use Loopback's built-in header validation:
import {HttpErrors} from '@loopback/rest';
async safeResponse(@param.query.string message: string) {
const crlfRegex = /%0D%0A|\r\n|\n\r|%0D|%0A|\r|\n/gi;
if (crlfRegex.test(message)) {
throw new HttpErrors.BadRequest('Invalid input detected');
}
this.response.set('X-Custom-Header', message);
this.response.send('OK');
}Create a reusable utility for CRLF sanitization across your Loopback application:
export function sanitizeCrlf(input: string): string {
const crlfRegex = /%0D%0A|\r\n|\n\r|%0D|%0A|\r|\n/gi;
return input.replace(crlfRegex, '');
}
// Usage in controllers
import {sanitizeCrlf} from '../utils/security';
async secureEndpoint(@param.query.string data: string) {
const safeData = sanitizeCrlf(data);
// Continue processing with safeData
}Loopback's middleware system provides another layer of protection. Create a global CRLF filter middleware:
export async function crlfFilterMiddleware(
ctx: RequestContext,
next: () => Promise
) {
const crlfRegex = /%0D%0A|\r\n|\n\r|%0D|%0A|\r|\n/gi;
// Sanitize query parameters
const sanitizedQuery: Record = {};
for (const [key, value] of Object.entries(ctx.request.query)) {
if (typeof value === 'string') {
sanitizedQuery[key] = value.replace(crlfRegex, '');
}
}
ctx.request.query = sanitizedQuery;
// Sanitize request body for JSON requests
if (ctx.request.hasBody) {
const body = await ctx.request.body({}).value;
if (body && typeof body === 'object') {
const sanitizedBody = JSON.parse(
JSON.stringify(body).replace(crlfRegex, '')
);
ctx.request.body = () => Promise.resolve(sanitizedBody);
}
}
await next();
} Register this middleware in your Loopback application's sequence to provide automatic CRLF protection across all endpoints.
For maximum security, combine these approaches: use Loopback's parameter validation, apply sanitization utilities, implement global middleware, and regularly scan with middleBrick to ensure new vulnerabilities aren't introduced.
Frequently Asked Questions
How does middleBrick detect CRLF injection in Loopback applications?
Can CRLF injection in Loopback lead to authentication bypass?
Set-Cookie: authenticated=true or manipulating Authorization headers through response splitting can grant unauthorized access. This is why middleBrick includes specific tests for authentication-related header manipulation in its scanning.