Crlf Injection in Nestjs with Bearer Tokens
Crlf Injection in Nestjs with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into a header or status-line context. In NestJS, this often arises when dynamic values—such as tokens, identifiers, or user-controlled strings—are reflected into HTTP response headers without proper sanitization. When Bearer Tokens are involved, developers sometimes pass token material (or token-like strings) into header construction logic, for example when building custom authorization headers, logging, or error responses.
Consider a NestJS service that forwards an incoming Authorization header to an upstream service and echoes a token value into a custom response header. If the token value contains \r or \n characters (which can happen with malformed tokens, crafted tokens during testing, or attacker-controlled inputs), and the framework or underlying HTTP client does not sanitize them, the injected CRLF sequence can split the header block. This allows an attacker to inject additional headers or even split the response body, leading to HTTP response splitting, header manipulation, or XSS via injected Set-Cookie or Location headers.
In practice, this becomes a risk when token values are used in header-related operations such as:
- Setting custom headers like
x-access-tokenorAuthorizationin outgoing requests from your NestJS server. - Logging tokens into response headers for debugging without validation.
- Returning tokens in Location or Set-Cookie headers during redirects or auth flows.
Because Bearer Tokens are often opaque strings, they may include characters that can be misinterpreted as control characters if concatenated directly into header lines. NestJS does not inherently sanitize user input used in header construction, so it is the developer’s responsibility to validate and encode any dynamic values that may reach HTTP headers. Attack patterns such as response splitting can enable cache poisoning, cross-user contamination, or bypass of security controls when CRLF characters are injected into headers.
Real-world examples include scenarios where an attacker supplies a token like abc\r\nSet-Cookie: session=evil and the server places it into a header, effectively injecting a new cookie. Another case is when log entries containing tokens with embedded CRLF are reflected in debugging endpoints that expose headers, leading to information disclosure or smuggling.
Bearer Tokens-Specific Remediation in Nestjs — concrete code fixes
Remediation focuses on strict input validation, avoiding direct concatenation of token values into headers, and using framework-safe abstractions. Do not rely on manual string assembly when setting headers. Instead, use typed methods and validation libraries to ensure tokens and other inputs do not contain control characters.
1. Validate and sanitize token inputs
Use a validation pipe to reject tokens containing CR or LF characters. For custom DTOs that may carry token-like values, enforce a strict pattern.
import { PipeTransform, BadRequestException } from '@nestjs/common';
export class SanitizeTokenPipe implements PipeTransform {
transform(value: string): string {
if (typeof value !== 'string') {
throw new BadRequestException('Token must be a string');
}
if (/[\r\n]/.test(value)) {
throw new BadRequestException('Token contains invalid characters');
}
return value;
}
}
Apply it in your controller or service layer when accepting token inputs.
2. Use safe header-setting patterns
Avoid manually concatenating strings into header values. Use Node’s built-in mechanisms or HTTP client libraries that handle header safety. For outgoing HTTP requests, prefer typed header objects.
import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProxyService {
constructor(private readonly httpService: HttpService) {}
forwardWithBearer(token: string) {
// Safe: headers are set as an object; no manual line assembly
return this.httpService.get('https://api.example.com/me', {
headers: {
Authorization: `Bearer ${token.replace(/[\r\n]/g, '')}`,
},
});
}
}
If you must set headers in an outgoing Node HTTP client, ensure token values are sanitized before inclusion. The example above removes CR/LF characters defensively while preserving the expected token format.
3. Avoid exposing tokens in logging or error responses
Ensure token values are not reflected in response headers, logs, or error messages that might be returned to the client. Use structured logging that redacts sensitive values.
import { Logger } from '@nestjs/common';
const logger = new Logger('AuthService');
export class AuthService {
logTokenContext(token: string) {
// Safe: do not log raw token if it may contain control characters
const safe = token.replace(/[\r\n]/g, '');
logger.debug(`Token context processed, length=${safe.length}`);
}
}
4. Secure redirects and Location headers
If your flow involves redirects, validate that Location or Referer headers do not incorporate untokenized input. Prefer whitelisted redirect targets.
import { Redirect } from '@nestjs/common';
// Prefer fixed or validated redirect targets; avoid reflecting tokens into Location
@Redirect('https://app.example.com/dashboard')
export class AuthController {
// Do not compute redirect from raw token values
}