{"record":{"id":"bb2c0290e63bc671","repo":"symfony/http-foundation","slug":"invalid-uri-a-uri-cannot-contain-cr-lf-tab-characters","errorCode":null,"errorMessage":"Invalid URI: A URI cannot contain CR/LF/TAB characters.","messagePattern":"Invalid URI: A URI cannot contain CR/LF/TAB characters\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"Request.php","lineNumber":412,"sourceCode":"            throw new BadRequestException('Invalid URI: Scheme is malformed.');\n        }\n        if (false === $components = parse_url(\\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) {\n            throw new BadRequestException('Invalid URI.');\n        }\n\n        $part = ($components['user'] ?? '').':'.($components['pass'] ?? '');\n\n        if (':' !== $part && \\strlen($part) !== strcspn($part, '[]')) {\n            throw new BadRequestException('Invalid URI: Userinfo is malformed.');\n        }\n        if (($part = $components['host'] ?? '') && !self::isHostValid($part)) {\n            throw new BadRequestException('Invalid URI: Host is malformed.');\n        }\n        if (false !== ($i = strpos($uri, '\\\\')) && $i < strcspn($uri, '?#')) {\n            throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.');\n        }\n        if (\\strlen($uri) !== strcspn($uri, \"\\r\\n\\t\")) {\n            throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.');\n        }\n        if ('' !== $uri && (\\ord($uri[0]) <= 32 || \\ord($uri[-1]) <= 32)) {\n            throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.');\n        }\n\n        if (isset($components['host'])) {\n            $server['SERVER_NAME'] = $components['host'];\n            $server['HTTP_HOST'] = $components['host'];\n        }\n\n        if (isset($components['scheme'])) {\n            if ('https' === $components['scheme']) {\n                $server['HTTPS'] = 'on';\n                $server['SERVER_PORT'] = 443;\n            } else {\n                unset($server['HTTPS']);\n                $server['SERVER_PORT'] = 80;\n            }","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/symfony/http-foundation/blob/5aea19cd678fa4140f6108406f1096de5e9ed6e4/Request.php#L394-L430","documentation":"Request::create() forbids raw CR (\\r), LF (\\n) and TAB characters anywhere in the URI. These characters enable HTTP request-splitting/response-splitting (CRLF injection) when the URI is written into the request line or headers, so the library throws BadRequestException ('Invalid URI: A URI cannot contain CR/LF/TAB characters.') — an HTTP 400 — instead of building the Request.","triggerScenarios":"Calling Request::create() with a URI containing \"\\r\", \"\\n\" or \"\\t\", typically from unchecked user input interpolated into a URL (e.g. '/redirect?url=http://x\\r\\nHost: evil') — strlen($uri) !== strcspn($uri, \"\\r\\n\\t\") at Request.php:410-412. Multi-line pasted URLs and log-injected payloads also trigger it.","commonSituations":"Redirect endpoints taking a full URL from a query parameter; header injection attempts caught by the framework; URLs read from files or databases with trailing newlines; templates that concatenate user data into hrefs that are later fed to Request::create().","solutions":["Trim and strip control characters from the URI before use: trim($uri), or reject if preg_match('/[\\r\\n\\t]/', $uri).","URL-encode dynamic parts (rawurlencode) so CR/LF/TAB become %0D/%0A/%09 instead of raw bytes.","Fix the upstream data source that carries trailing newlines (e.g. rtrim() values read from files or DB rows).","Catch BadRequestException for untrusted URLs and return a 400; log the attempt as a likely CRLF-injection probe."],"exampleFix":"// before\n$request = Request::create($userProvidedUri); // may contain \"\\r\\n\"\n// after\nif (preg_match('/[\\r\\n\\t]/', $userProvidedUri)) {\n    throw new \\InvalidArgumentException('URI contains control characters');\n}\n$request = Request::create(trim($userProvidedUri));","handlingStrategy":"validation","validationCode":"function isCrlfSafe(string $uri): bool\n{\n    return strlen($uri) === strcspn($uri, \"\\r\\n\\t\");\n}\nif (!isCrlfSafe($uri)) {\n    throw new \\InvalidArgumentException('URI contains CR/LF/TAB');\n}\n$request = Request::create($uri);","typeGuard":"function isSingleLineUri(mixed $uri): bool\n{\n    return \\is_string($uri) && '' !== $uri && !preg_match('/[\\x00-\\x1F]/', $uri);\n}","tryCatchPattern":"try {\n    $request = Request::create($uri);\n} catch (BadRequestException $e) {\n    if (str_contains($e->getMessage(), 'CR/LF/TAB')) {\n        // Likely CRLF-injection attempt; do not retry with the same input\n        return new Response('Invalid characters in URL.', 400);\n    }\n\n    throw $e;\n}","preventionTips":["rtrim()/trim() any URI read from files, databases, or headers before use","rawurlencode() every user-controlled value interpolated into a URL","Treat CR/LF in a URL from a client as an injection attempt and log it","Run header-injection test cases (\\r\\nHost: evil) against redirect and proxy endpoints"],"tags":["http","uri-validation","security","crlf-injection"],"backgroundTag":"invalid-url-format","analyzedSha":"5aea19cd678fa4140f6108406f1096de5e9ed6e4","analyzedAt":"2026-09-13T01:52:22.855Z","contentChangedAt":"2026-09-13T01:52:22.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}