Crlf Injection in Laravel
How Crlf Injection Manifests in Laravel
CRLF injection in Laravel applications typically occurs when user-controlled data is included in HTTP headers without proper sanitization. The attack exploits the fact that HTTP headers are separated by CRLF (Carriage Return Line Feed, or \r\n) sequences, allowing attackers to inject additional headers or modify existing ones.
In Laravel, common vulnerability points include:
- Redirect responses with user-controlled URLs in
redirect()->away()orredirect()->to() - Response headers manipulated via
header()orwithHeaders() - Set-Cookie headers with unvalidated user input
- Email headers in Laravel's Mail facade when using raw headers
Here's a vulnerable Laravel code example:
// Vulnerable: User input in Location header
public function redirectWithParam(Request $request) {
$url = $request->input('redirect_url');
return redirect()->away($url);
}
// Vulnerable: Header injection via user input
public function setCustomHeader(Request $request) {
$headerName = $request->input('header_name');
$headerValue = $request->input('header_value');
return response('OK')
->withHeaders([$headerName => $headerValue]);
}
// Vulnerable: Email header injection
public function sendEmail(Request $request) {
$email = $request->input('email');
$subject = $request->input('subject');
$message = $request->input('message');
Mail::raw("Subject: {$subject}\n\n{$message}", function ($message) use ($email) {
$message->to($email);
});
}An attacker could exploit these by sending requests with encoded CRLF sequences:
# HTTP header injection
curl -X GET "http://example.com/redirect?redirect_url=http://evil.com%0d%0aSet-Cookie:%20session=evil"This would inject a malicious Set-Cookie header into the response, potentially hijacking user sessions.
Laravel-Specific Detection
Detecting CRLF injection in Laravel requires both manual code review and automated scanning. Key detection strategies include:
- Static Analysis: Search for header-related methods and user input concatenation
- Dynamic Testing: Send requests with encoded CRLF sequences and observe responses
- Automated Scanning: Use specialized tools that understand Laravel's patterns
Manual detection checklist for Laravel:
// Search for these patterns in your codebase:
// 1. redirect()->away() with user input
// 2. redirect()->to() with user input
// 3. withHeaders() calls
// 4. header() function usage
// 5. Mail::raw() with user input in headers
// 6. Response::header() calls
// Look for these dangerous patterns:
$userInput . "\r\n" // direct concatenation
urlencode($userInput) // improper encoding
base64_decode($userInput) // decoding without validationmiddleBrick offers specialized detection for Laravel applications through its automated scanning platform. The scanner identifies CRLF injection vulnerabilities by:
- Testing header injection points with encoded CRLF sequences
- Analyzing Laravel-specific response patterns and redirect methods
- Checking email functionality for header injection
- Providing Laravel-specific remediation guidance based on the detected vulnerability
To scan a Laravel API with middleBrick:
# Using the CLI tool
middlebrick scan https://your-laravel-app.com/api
# Using the GitHub Action
- name: Scan Laravel API
uses: middlebrick/middlebrick-action@v1
with:
api_url: 'https://your-laravel-app.com/api'
fail_threshold: 'C'
The scanner tests for CRLF injection by sending payloads like:
%0d%0aSet-Cookie:%20session=malicious
%0d%0aContent-Length:%200%0d%0aX-Injected-Header:%20valueand analyzes the responses for successful header injection.
Laravel-Specific Remediation
Remediating CRLF injection in Laravel involves input validation, proper encoding, and using Laravel's built-in security features. Here are specific fixes for common vulnerability patterns:
1. Secure Redirects
// Vulnerable
public function redirectWithParam(Request $request) {
$url = $request->input('redirect_url');
return redirect()->away($url);
}
// Secure
public function redirectWithParam(Request $request) {
$url = $request->input('redirect_url');
// Validate URL scheme and host
if (!filter_var($url, FILTER_VALIDATE_URL) ||
!in_array(parse_url($url, PHP_URL_SCHEME), ['http', 'https'])) {
abort(400, 'Invalid redirect URL');
}
return redirect()->away($url);
}
// Alternative: Whitelist allowed URLs
public function redirectWithParam(Request $request) {
$url = $request->input('redirect_url');
$allowedHosts = ['example.com', 'app.example.com'];
$parsedUrl = parse_url($url);
if (!$parsedUrl || !in_array($parsedUrl['host'], $allowedHosts)) {
abort(400, 'Invalid redirect URL');
}
return redirect()->away($url);
}2. Safe Header Manipulation
// Vulnerable
public function setCustomHeader(Request $request) {
$headerName = $request->input('header_name');
$headerValue = $request->input('header_value');
return response('OK')
->withHeaders([$headerName => $headerValue]);
}
// Secure
public function setCustomHeader(Request $request) {
$headerName = $request->input('header_name');
$headerValue = $request->input('header_value');
// Validate header name
$allowedHeaders = ['X-Custom-Header', 'X-Request-ID'];
if (!in_array($headerName, $allowedHeaders)) {
abort(400, 'Invalid header name');
}
// Sanitize header value (remove CRLF)
$sanitizedValue = preg_replace('/[
]+/', '', $headerValue);
return response('OK')
->header($headerName, $sanitizedValue);
}3. Secure Email Headers
// Vulnerable
public function sendEmail(Request $request) {
$email = $request->input('email');
$subject = $request->input('subject');
$message = $request->input('message');
Mail::raw("Subject: {$subject}\n\n{$message}", function ($message) use ($email) {
$message->to($email);
});
}
// Secure
public function sendEmail(Request $request) {
$email = $request->input('email');
$subject = $request->input('subject');
$message = $request->input('message');
// Validate email format
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
abort(400, 'Invalid email address');
}
// Sanitize subject (remove CRLF)
$sanitizedSubject = preg_replace('/[
]+/', ' ', $subject);
// Use Laravel's proper mail methods instead of raw
Mail::raw($message, function ($m) use ($email, $sanitizedSubject) {
$m->to($email)
->subject($sanitizedSubject);
});
}4. Middleware for Global Protection
// Create a middleware to sanitize headers globally
php artisan make:middleware CrlfProtectionMiddleware
// app/Http/Middleware/CrlfProtectionMiddleware.php
public function handle($request, Closure $next) {
$response = $next($request);
// Sanitize all headers in the response
foreach ($response->headers->all() as $name => $values) {
$sanitizedValues = array_map(function($value) {
return preg_replace('/[
]+/', '', $value);
}, $values);
$response->headers->set($name, $sanitizedValues);
}
return $response;
}
// Register in app/Http/Kernel.php
protected $middleware = [
// ... other middleware
\App\Http\Middleware\CrlfProtectionMiddleware::class,
];