Mar 23, 2026·6 min read·5 visits
The H3 `redirectBack()` function improperly handles protocol-relative paths in the `Referer` header. Attackers can supply a referer like `http://trusted.com//evil.com`, passing origin checks but resulting in a `Location: //evil.com` header that browsers interpret as a redirect to an external site.
A critical vulnerability in the UnJS H3 package allows attackers to bypass domain validation within the `redirectBack()` utility. By providing a maliciously crafted `Referer` header containing a protocol-relative path, attackers can force the application to issue an open redirect to an arbitrary external domain. This issue affects versions prior to v2.0.1-rc.18.
The UnJS H3 framework is a minimal HTTP framework for Node.js, extensively utilized within the Nuxt ecosystem. The framework exposes a redirectBack() utility function designed to safely redirect users back to the page specified in their Referer header. To prevent malicious redirection, this function validates that the origin of the Referer URL exactly matches the origin of the current request.
The vulnerability, classified under CWE-601 (URL Redirection to Untrusted Site), occurs because the validation logic fails to account for protocol-relative paths extracted from the WHATWG URL object. While the origin validation correctly ensures the protocol and hostname match the trusted application, the pathname property retains leading double slashes if provided in the input.
When this unvalidated pathname is concatenated and passed directly into the Location HTTP response header, the context shifts. Modern web browsers interpret a Location header beginning with // as a protocol-relative URL, instructing the browser to maintain the current scheme (e.g., HTTPS) but navigate to a completely different host. This bypasses the developer's intended domain restrictions.
The root cause of this vulnerability lies in an impedance mismatch between how the WHATWG URL constructor parses malformed paths and how web browsers interpret HTTP Location headers. When the native Node.js URL class processes a string like https://trusted-app.com//malicious-site.com/steal, it correctly identifies the protocol as https: and the host as trusted-app.com.
Because the host matches the expected origin, the application's security check (refererURL.origin === event.url.origin) evaluates to true. However, the URL constructor parses the remainder of the string, //malicious-site.com/steal, and assigns it to the pathname property without normalizing the leading slashes. The application then uses this raw pathname to construct the redirect destination.
According to RFC 3986, a path segment beginning with two slashes is a network-path reference, commonly known as a protocol-relative URL. When the server sets Location: //malicious-site.com/steal, the browser resolves this relative to the current scheme. If the user is browsing via HTTPS, the browser redirects the user to https://malicious-site.com/steal, effectively completing the Open Redirect attack.
In versions of H3 prior to v2.0.1-rc.18, the redirectBack() function extracts the pathname directly from the validated refererURL object and applies it to the Location header. The vulnerable code path assumes that since the origin is trusted, the associated path is inherently local and safe for redirection.
The patch introduced in commit 459a1c6593365b0810e9c502df7c3e82837321d7 introduces an explicit normalization step for the pathname string. The implementation now checks if the path begins with a protocol-relative sequence (//) and strips all leading slashes before prepending a single, safe forward slash.
// File: src/utils/response.ts
export function redirectBack(event, opts = {}) {
// ... existing origin validation logic ...
if (refererURL.origin === event.url.origin) {
let pathname = refererURL.pathname;
// PATCH: Sanitize protocol-relative paths
if (pathname.startsWith("//")) {
pathname = "/" + pathname.replace(/^\/+/, "");
}
location = pathname + (opts.allowQuery ? refererURL.search : "");
}
// ...
}This regex-based fix (/^\/+/) is highly effective because it ensures that regardless of how many slashes an attacker injects (e.g., ///evil.com), the resulting path is strictly forced into an absolute path relative to the current origin (e.g., /evil.com). This completely neutralizes the protocol-relative behavior in the browser's Location parser.
Exploiting this Open Redirect requires an attacker to control the Referer header sent by the victim's browser. While browsers typically restrict direct manipulation of the Referer header via client-side scripts, an attacker can construct a malicious webpage that naturally generates the desired header when the victim clicks a link.
The attack begins by hosting a staging page that initiates a request to the vulnerable H3 application endpoint. The staging environment is configured to append the malicious protocol-relative payload to its URL structure, or the attacker crafts a direct link containing the payload structure if the application passes user-controlled parameters into the Referer generation.
The following proof-of-concept test case demonstrates the exact server-side behavior. When the malicious referer http://localhost//evil.com/steal is submitted, the unpatched server responds with an unsafe Location header.
it("prevents open redirect via protocol-relative path in referer", async () => {
// Application endpoint using redirectBack
t.app.post("/submit", (event) => redirectBack(event));
const baseUrl = "http://localhost";
const res = await t.fetch("/submit", {
method: "POST",
headers: {
referer: `${baseUrl}//evil.com/steal`
},
});
// Unpatched response returns Location: //evil.com/steal
// Patched response returns Location: /evil.com/steal
});The primary impact of this vulnerability is the facilitation of highly convincing phishing campaigns. Because the initial URL points to a trusted domain (the application running H3), users and email security filters are significantly more likely to trust the link. Once clicked, the application transparently forwards the user to an attacker-controlled destination.
Beyond basic phishing, Open Redirects often serve as chaining primitives in more complex attack sequences. If the vulnerable application utilizes OAuth or similar token-based authentication flows, an attacker can manipulate the redirect to hijack authorization codes or access tokens. By appending token fragments to the redirected URL, the attacker captures sensitive credentials on their own infrastructure.
The vulnerability is assessed with a CVSS 3.1 score of 6.1 (Medium). The attack vector is Network (AV:N) with Low attack complexity (AC:L), requiring no specialized privileges (PR:N). However, it strictly requires User Interaction (UI:R) and affects the changed scope of the user's browsing session (S:C), leading to low impacts on Confidentiality and Integrity (C:L/I:L).
The definitive remediation for this vulnerability is upgrading the h3 package to version v2.0.1-rc.18 or later. Development teams should audit their package.json or package-lock.json files to ensure all transitive dependencies utilizing h3 are updated to incorporate the patched logic.
For environments where immediate patching is not technically feasible, a temporary mitigation involves deploying middleware or a Web Application Firewall (WAF) rule to intercept and sanitize incoming requests. Specifically, rules should inspect the Referer header and reject or rewrite any requests where the pathname segment begins with multiple forward slashes.
Developers utilizing redirectBack() should also review their application architecture to determine if relying on the Referer header is strictly necessary. Implementing stateful redirection flows using server-side session variables or signed redirect tokens provides a more robust defense-in-depth posture against redirection attacks.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
h3 UnJS | < v2.0.1-rc.18 | v2.0.1-rc.18 |
| Attribute | Detail |
|---|---|
| Vulnerability Class | Open Redirect (CWE-601) |
| CVSS v3.1 Score | 6.1 (Medium) |
| Attack Vector | Network |
| Authentication Required | None |
| User Interaction | Required |
| Affected Component | redirectBack() utility |
| Exploit Status | Proof of Concept Available |
A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.