CRLF injection (a.k.a. HTTP Response Splitting) happens when untrusted input is written into HTTP headers. Attackers smuggle \r\n
(CRLF) to forge extra headers or split responses—leading to cache poisoning, open redirects, or cookie shenanigans. Here’s a concise, dev-friendly guide for Symfony.
How it slips in
A classic trap is building headers from user input (redirects, filenames, cookies) without sanitizing line breaks.
❌ Vulnerable example
// src/Controller/AuthController.php
use Symfony\Component\HttpFoundation\Response;
#[Route('/login', name: 'login')]
public function login(Request $req): Response {
$next = $req->query->get('next', '/');
$resp = new Response('', 302);
// DANGER: $next could contain \r\n
$resp->headers->set('Location', $next);
return $resp;
}
🧪 Safe PoC (local only!)
Request:
/login?next=%2Fhome%0D%0ASet-Cookie:%20isAdmin%3D1
If your stack fails to block CRLF, the response may include an injected Set-Cookie
.
Fix it right
1) Strip CRLF and validate
// utils/Safe.php
final class Safe {
public static function stripCRLF(string $v): string {
return preg_replace('/[\r\n]+/', '', $v);
}
}
// Controller hardening
$next = Safe::stripCRLF($req->query->get('next', '/'));
if (!preg_match('#^/[\w\-/]*$#', $next)) { // allow only internal paths
$next = '/';
}
$resp = new Response('', 302);
$resp->headers->set('Location', $next);
2) Prefer route-based redirects
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
public function goHome(UrlGeneratorInterface $urlGen): RedirectResponse {
$url = $urlGen->generate('homepage'); // server-side trusted
return new RedirectResponse($url, 302);
}
3) Encode dynamic header pieces
$filename = Safe::stripCRLF($req->query->get('file', 'report.txt'));
$disposition = sprintf('attachment; filename="%s"', addcslashes($filename, "\"\\"));
$response = new Response($content);
$response->headers->set('Content-Disposition', $disposition);
Testing checklist (fast)
- Reject
\r
or\n
anywhere in header-bound input. - Constrain redirects to internal paths or a strict allowlist.
- Add integration tests that assert absence of CR/LF in headers.
// tests/Security/HeaderTest.php
public function testNoCRLFInLocation(): void {
$client = static::createClient();
$client->request('GET', '/login?next=%0D%0Aevil');
$this->assertFalse(str_contains($client->getResponse()->headers->get('Location'), "\r"));
$this->assertFalse(str_contains($client->getResponse()->headers->get('Location'), "\n"));
}
See it with our free scanner
While you harden code, scan the running site to catch missed sinks.
Free Website Vulnerability Scanner homepage:
Screenshot of the free tools webpage where you can access security assessment tools.
Sample Vulnerability Report to check Website Vulnerability:
Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.
Try it: https://free.pentesttesting.com/
More reads: https://www.pentesttesting.com/blog/
Managed IT Services (New)
Need patching, monitoring, and incident response baked in? Explore:
https://www.pentesttesting.com/managed-it-services/
AI Application Cybersecurity (New)
Ship AI features without leaking prompts/tokens or enabling prompt-injection pivots:
https://www.pentesttesting.com/ai-application-cybersecurity/
Offer Cybersecurity to Your Clients (Agency)
White-label pentesting & security services—earn while we deliver:
https://www.pentesttesting.com/offer-cybersecurity-service-to-your-client/
Newsletter
Stay ahead on practical AppSec & pentest tactics:
Subscribe on LinkedIn https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7327563980778995713
Quick takeaway
- Never write raw user input into headers.
- Strip CR/LF, validate, and prefer framework-built redirects.
- Verify with tests and a runtime scan for defense-in-depth.
Top comments (0)