HIGH crlf injectionloopback

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?
middleBrick performs black-box scanning by sending payloads containing CRLF sequences to your Loopback endpoints. It monitors responses for header injection, response splitting, or other CRLF-related vulnerabilities. The scanner tests 12 security categories including authentication bypass through header manipulation, which is a common CRLF injection goal. No setup or credentials required—just submit your Loopback API URL.
Can CRLF injection in Loopback lead to authentication bypass?
Yes, CRLF injection can be used to bypass authentication in Loopback applications. An attacker could inject HTTP headers that manipulate session cookies, authorization tokens, or authentication status. For example, injecting 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.