Crlf Injection in Nestjs
How Crlf Injection Manifests in Nestjs
CRLF injection in NestJS applications occurs when untrusted user input containing carriage return ( ) and line feed ( ) characters is incorporated into HTTP headers, HTTP responses, or other protocol elements without proper sanitization. In NestJS, this vulnerability commonly manifests through several specific code patterns.
The most frequent occurrence is in response manipulation within controllers and interceptors. When developers dynamically construct headers or response bodies using user input, they inadvertently create injection points. For example, a controller method that echoes back request headers or query parameters directly into responses becomes vulnerable:
@Get('echo')
@Header('X-Custom-Header', req.headers['x-custom-header'])
async echo(@Req() req: Request) {
return {
message: req.query.message,
headers: req.headers
};
}In this pattern, an attacker could supply a message parameter like test%0D%0AContent-Type%3A%20text/html%0D%0A%0D%0A%3Cscript%3Ealert(1)%3C/script%3E, causing the server to inject additional headers and malicious content into the response.
Middleware and guards in NestJS present another attack surface. Custom middleware that logs request information or modifies responses based on user input can become injection vectors:
export class LoggingMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const userAgent = req.headers['user-agent'];
console.log(`User-Agent: ${userAgent}`);
next();
}
}If the user-agent header contains CRLF sequences, the log output could be manipulated, and in some logging configurations, this might affect downstream processing.
Exception filters in NestJS can also be exploited. When exceptions include user input in their messages or when custom error responses are constructed dynamically:
@Catch(HttpException)
export class CustomExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: Response) {
const response = exception.getResponse();
host.status(exception.getStatus()).json({
error: response.message,
timestamp: new Date().toISOString()
});
}
}If response.message contains CRLF characters, an attacker could manipulate the JSON structure or inject additional fields.
Header manipulation through @Header() decorator is particularly dangerous when values come from user input:
@Get('profile')
@Header('X-User-Role', req.user.role)
async getProfile(@Req() req: Request) {
return this.userService.getProfile(req.user.id);
}An attacker with ability to manipulate their role could inject CRLF sequences to add arbitrary headers like Content-Type: text/html or even Set-Cookie headers.
NestJS-Specific Detection
Detecting CRLF injection in NestJS applications requires both static code analysis and runtime scanning. Static analysis should focus on identifying code patterns where user input flows into response construction, header manipulation, or logging without sanitization.
middleBrick's scanning approach is particularly effective for NestJS applications because it performs black-box testing of the running API endpoints. The scanner automatically tests for CRLF injection by sending payloads containing various CRLF combinations to all endpoints and analyzing responses for protocol manipulation.
For manual detection in NestJS codebases, search for these patterns:
// Search for these patterns:
req.headers['...']
req.query['...']
req.params['...']
req.body['...']
@Header(...)
@Res() res
res.set(...)
res.header(...)
res.append(...)Then trace where these values are used in response construction or header manipulation. Pay special attention to logging statements, exception messages, and any code that echoes back user input.
middleBrick specifically tests for CRLF injection through 12 parallel security checks, including input validation and data exposure categories. The scanner sends payloads like:
%0D%0AContent-Type%3A%20text/html%0D%0A%0D%0A%3Cscript%3Ealert(1)%3C/script%3E
%0D%0ASet-Cookie%3A%20session=hacked
%0D%0ALocation%3A%20https://evil.comThe scanner then analyzes responses for evidence of header injection, content type manipulation, or script execution. For NestJS applications specifically, middleBrick's OpenAPI analysis can identify which endpoints accept user input that might flow into response construction.
CI/CD integration through the middleBrick GitHub Action allows continuous monitoring of CRLF injection vulnerabilities. You can configure the action to fail builds if new injection points are discovered, ensuring that CRLF vulnerabilities are caught before deployment.
NestJS-Specific Remediation
Remediating CRLF injection in NestJS applications requires a defense-in-depth approach combining input validation, output encoding, and secure coding practices. The most effective strategy is to implement validation at the controller level using NestJS's built-in features.
Use class-validator decorators to sanitize and validate input:
import { IsString, IsOptional, MaxLength } from 'class-validator';
export class MessageDto {
@IsString()
@MaxLength(500)
message: string;
@IsOptional()
@IsString()
@MaxLength(100)
customHeader: string;
}Create a global validation pipe to automatically sanitize input:
import { validate, ValidationError } from 'class-validator';
import { plainToClass } from 'class-transformer';
export class GlobalValidationPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
if (!value) return value;
const object = plainToClass(metadata.metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
}
// Additional CRLF sanitization
this.sanitizeInput(value);
return value;
}
private sanitizeInput(input: any): void {
if (typeof input === 'string') {
input = input.replace(/\r/g, '').replace(/\n/g, '');
} else if (typeof input === 'object') {
Object.keys(input).forEach(key => {
this.sanitizeInput(input[key]);
});
}
}
}
// In main.ts
app.useGlobalPipes(new GlobalValidationPipe());For header manipulation, create a secure header service:
import { Injectable } from '@nestjs/common';
@Injectable()
export class SecureHeaderService {
setHeader(res: Response, headerName: string, value: string): void {
// Sanitize header name and value
const sanitizedName = this.sanitizeHeaderName(headerName);
const sanitizedValue = this.sanitizeHeaderValue(value);
res.set(sanitizedName, sanitizedValue);
}
private sanitizeHeaderName(name: string): string {
return name.replace(/[^a-zA-Z0-9-_]/g, '');
}
private sanitizeHeaderValue(value: string): string {
return value.replace(/\r/g, '').replace(/\n/g, '');
}
}Implement content security policies to mitigate impact of any successful injection:
@Controller('api')
@UseGuards(AuthGuard)
@UseInterceptors(HeaderInterceptor)
export class ApiController {
constructor(private secureHeaderService: SecureHeaderService) {}
}For logging middleware, ensure proper sanitization:
export class SecureLoggingMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const userAgent = req.headers['user-agent']?.toString() || '';
const sanitizedUserAgent = userAgent.replace(/\r/g, '').replace(/\n/g, '');
console.log(`User-Agent: ${sanitizedUserAgent}`);
next();
}
}middleBrick's continuous monitoring in the Pro plan can verify that these remediations remain effective over time by regularly scanning your API endpoints for CRLF injection attempts.