symfony/http-foundation · error · BadRequestException
Invalid URI: Userinfo is malformed.
Error message
Invalid URI: Userinfo is malformed.
What it means
Request::create() validates every component of the URI it is asked to turn into a Request before constructing it. This check guards against userinfo-spoofing/open-redirect tricks: if the userinfo part (user:pass before @) contains '[' or ']', the URI is ambiguous or malicious (e.g. 'http://normal.com[@evil.com/') and the library rejects it with a BadRequestException (HTTP 400). It means the caller passed a syntactically unsafe URI string, not a transient failure.
Solutions
- URL-encode or remove '[' and ']' from the userinfo (user:password) portion of the URI before passing it to Request::create().
- Use rawurlencode() on the username and password separately and rebuild the URI: scheme://rawurlencode($user).':'.rawurlencode($pass).'@host'.
- If the brackets are meant for the host (IPv6 literal like http://[::1]/), ensure they are in the host component, not the userinfo, and that the URI parses as intended.
- Wrap the call in try/catch for BadRequestException when URLs come from untrusted input and return a 400 to the client.
Example fix
// before
$request = Request::create('http://normal.com[@vulndetector.com/');
// after
$uri = 'http://' . rawurlencode('normal.com[') . '@vulndetector.com/';
// or better: reject it up front
if (preg_match('#^[a-z][a-z0-9+.-]*://[^/?#@]*\[|^https?://[^/?#]*\][^/]#i', $uri)) {
throw new \InvalidArgumentException('URI userinfo contains brackets');
}
$request = Request::create($uri); Defensive patterns
Strategy: validation
Validate before calling
function hasSafeUserinfo(string $uri): bool
{
$u = parse_url($uri);
if (!isset($u['user']) && !isset($u['pass'])) {
return true;
}
$part = ($u['user'] ?? '') . ':' . ($u['pass'] ?? '');
return ':' === $part || strlen($part) === strcspn($part, '[]');
}
if (!hasSafeUserinfo($uri)) {
throw new \InvalidArgumentException('URI userinfo contains brackets');
} Type guard
function isCreatableUri(string $uri): bool
{
return '' !== $uri
&& false === strpos($uri, '\\')
&& strlen($uri) === strcspn($uri, "\r\n\t")
&& ord($uri[0]) > 32 && ord($uri[-1]) > 32;
} Try / catch
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
try {
$request = Request::create($uri);
} catch (BadRequestException $e) {
return new Response('Invalid URL supplied.', 400);
} Prevention
- Always rawurlencode() username and password when embedding credentials in a URI
- Validate untrusted URLs (host, userinfo, no control chars) before handing them to Request::create()
- Treat brackets in userinfo as a hostile-input signal and reject rather than repair
- Add unit tests with the spoofing payloads 'http://a[@b/' and 'http://[a@b/' to your URL ingestion code
When it happens
Trigger: Calling Request::create() (or Request::createRequestUri-based factory paths) with a URI whose userinfo component contains square brackets, e.g. 'http://user[name]:pass@host/' or spoofing forms like 'http://normal.com[@vulndetector.com/' or 'http://[normal.com@vulndetector.com/'. parse_url() accepted the URI but the strict post-parse check in Request.php:401-403 (strlen($part) !== strcspn($part, '[]')) fails.
Common situations: Constructing requests programmatically from user-supplied URLs; SSRF/open-redirect test suites; proxies or crawlers feeding raw URLs into Request::create(); copy-pasted URLs containing brackets in credentials; security scanners deliberately probing host-header confusion.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid URI: A URI cannot contain a backslash.
- Invalid URI: Host is malformed.
- Invalid URI: A URI cannot contain CR/LF/TAB characters.
- Invalid URI: A URI must not start nor end with ASCII…
- Invalid URI: Scheme is malformed.
AI-assisted analysis of symfony/http-foundation@5aea19cd67 (2026-09-13).
Data as JSON: /api/errors/2f89fc5fbf784158.
Report an issue: GitHub.
Appendix: source
Thrown at Request.php:403
'SERVER_PROTOCOL' => 'HTTP/1.1',
'REQUEST_TIME' => time(),
'REQUEST_TIME_FLOAT' => microtime(true),
], $server);
$server['PATH_INFO'] = '';
$server['REQUEST_METHOD'] = strtoupper($method);
if (($i = strcspn($uri, ':/?#')) && ':' === ($uri[$i] ?? null) && (strspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.') !== $i || strcspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'))) {
throw new BadRequestException('Invalid URI: Scheme is malformed.');
}
if (false === $components = parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) {
throw new BadRequestException('Invalid URI.');
}
$part = ($components['user'] ?? '').':'.($components['pass'] ?? '');
if (':' !== $part && \strlen($part) !== strcspn($part, '[]')) {
throw new BadRequestException('Invalid URI: Userinfo is malformed.');
}
if (($part = $components['host'] ?? '') && !self::isHostValid($part)) {
throw new BadRequestException('Invalid URI: Host is malformed.');
}
if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) {
throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.');
}
if (\strlen($uri) !== strcspn($uri, "\r\n\t")) {
throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.');
}
if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32)) {
throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.');
}
if (isset($components['host'])) {
$server['SERVER_NAME'] = $components['host'];
$server['HTTP_HOST'] = $components['host'];
}View on GitHub (pinned to 5aea19cd67)