Crlf Injection in Laravel (Php)
Crlf Injection in Laravel with Php — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when user-controlled data is placed into HTTP headers without proper sanitization, allowing an attacker to inject additional header lines using carriage return (CR, \r) and line feed (\n) sequences. In a Laravel application written in Php, this commonly arises when dynamic values—such as redirect URLs, filenames, or custom headers—are passed into header or cookie-setting functions. Laravel's helpers and underlying Symfony components often forward these values to native PHP functions like header() and setcookie(), which do not inherently validate that header values are free of injected line breaks.
Because HTTP headers are separated by \r\n, injecting these characters enables splitting or smuggled responses. For example, an attacker-controlled redirect URL like "https://example.com\r\nSet-Cookie: session=attacker" can cause Laravel to emit two separate headers when passed to the header() function. The response sent to the client will then include both the intended Location header and the malicious Set-Cookie, potentially bypassing intended security boundaries. This is especially risky when Laravel routes or controllers reflect input directly into headers without validation or encoding. Even when using higher-level abstractions such as redirect()->to($url), if the $url is user-supplied and not validated, the underlying call can ultimately result in a header() call with unsanitized content.
Another common scenario involves response streams or chunked transfer handling where multiple header() calls are made based on user input, such as custom request identifiers or filenames in Content-Disposition. If newlines are not stripped, an attacker can inject additional headers like X-Content-Type-Options or even split the response body, leading to response splitting or header smuggling. In a Laravel context, this can manifest in error handling paths or logging routines where input is concatenated into headers. Because the vulnerability is about how input reaches low-level header-setting routines, the risk persists regardless of Laravel middleware protections unless input is explicitly validated and sanitized at the point of use.
Php-Specific Remediation in Laravel — concrete code fixes
Remediation focuses on ensuring that any user-controlled data placed into HTTP headers is sanitized to remove or encode CR and LF characters before being passed to header() or setcookie(). In Php, the canonical approach is to disallow CR and LF characters outright when handling strings that will become headers or cookie values. Below are concrete, Laravel-friendly examples demonstrating safe practices.
1. Validating and sanitizing redirect URLs
Instead of passing raw user input to redirect()->to(), normalize and validate the URL. Reject or sanitize any CR or LF characters before use.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class SafeRedirectController extends Controller
{
public function redirect(Request $request)
{
$inputUrl = $request->input('url');
// Reject if URL contains CR or LF
if (preg_match('/[\r\n]/', $inputUrl)) {
return response('Invalid URL', 400);
}
// Optionally enforce a whitelist of allowed hosts
if (! Str::startsWith($inputUrl, ['https://trusted.com/', 'https://api.example.com/'])) {
return response('Not allowed', 403);
}
return redirect($inputUrl);
}
}
?>
2. Sanitizing custom headers and cookies
When setting headers or cookies, strip CR and LF characters and avoid concatenating user input directly into header strings.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Str;
class SanitizeHeaders
{
public function handle($request, Closure $next)
{
$raw = $request->input('x_custom');
// Remove CR and LF to prevent header injection
$safe = preg_replace('/[\r\n]/', '', $raw);
// Optionally enforce a safe pattern
if ($safe !== $raw) {
return response('Invalid characters in header value', 400);
}
header('X-Custom: ' . $safe);
return $next($request);
}
}
// For cookies, use Laravel's cookie helper with explicit character filtering
use Illuminate\Support\Facades\Cookie;
$value = preg_replace('/[\r\n]/', '', $request->input('tracking'));
cookie()->queue('tracking_id', $value, 60);
?>
3. Defensive output for filenames in Content-Disposition
When generating file downloads, ensure filenames do not contain newline characters and prefer ASCII-safe naming.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ExportController extends Controller
{
public function download(Request $request)
{
$name = $request->input('filename', 'report');
// Remove CR/LF and non-safe characters
$name = preg_replace('/[\r\n]/', '', $name);
$name = preg_replace('/[^\x20-\x7E]/', '_', $name);
$content = 'report data here';
return response($content)
->header('Content-Type', 'text/plain')
->header('Content-Disposition', 'attachment; filename="' . $name . '"');
}
}
?>