Feb 19, 2026·6 min read·9 visits
The 'RedirectSlashes' middleware in go-chi/chi versions 5.2.2 through 5.2.3 fails to sanitize backslashes. An attacker can use a payload like '/\evil.com/' which the server normalizes to '/\evil.com'. Browsers interpret this as a protocol-relative URL (effectively '//evil.com'), redirecting the victim to an external malicious site.
A logic flaw in the popular Go router 'chi' allows attackers to bypass URL sanitization and trigger open redirects. By exploiting browser normalization quirks and a weak string trimming implementation, an attacker can craft a payload that looks like a relative path to the server but acts as an absolute URL in the browser.
In the world of web development, we are obsessed with hygiene. We want our URLs clean, our slashes normalized, and our SEO scores optimized. The go-chi/chi router, a lightweight and idiomatic router for Go, offers a handy piece of middleware called RedirectSlashes. Its job is simple: take a messy URL like /api/users/// and politely guide the user to /api/users. It's the digital equivalent of a frantic host tidying up the living room before guests arrive.
But here is the thing about tidying up: if you sweep the dust under the rug, you haven't actually cleaned anything; you've just hidden the mess until someone trips over the lump. In this specific case, the RedirectSlashes middleware was so focused on managing forward slashes (/) that it completely forgot about the existence of the backslash (\).
This oversight turns a feature designed to improve user experience into a classic Open Redirect vulnerability. It is not the kind of bug that will dump your database or grant you a root shell (sorry, RCE hunters), but it is exactly the kind of bug that turns your trusted domain into a launchpad for phishing campaigns. It is a subtle disagreement between how a Go server sees a string and how a Chrome browser sees a URL.
To understand this vulnerability, you have to understand the mechanism of failure. The middleware intends to strip trailing slashes and ensure the path starts with a root slash. It does this to prevent duplicate content issues (SEO) and to keep routing logic strict. The flaw lies in the assumption that the only character used to delimit paths is the forward slash.
The vulnerability exists because RedirectSlashes uses Go's strings.Trim(path, "/"). This function creates a 'cutset' containing only the forward slash. It aggressively eats any forward slashes at the beginning or end of the string. However, it leaves backslashes completely untouched. If an attacker sends a request to https://target.com/\evil.com/, the server sees a path that needs cleaning.
Here is the logic flow that leads to disaster:
/\evil.com// and the trailing /. It does not strip the \ because it wasn't in the cutset. The string becomes \evil.com./\evil.com.301 Moved Permanently to Location: /\evil.com.Now, if you or I look at /\evil.com, we might see a relative path on the file system. But web browsers are built on decades of legacy compatibility code. When Chrome, Firefox, or Edge see a URL starting with /\, they treat it as a Protocol-Relative URL, identical to starting with //. The browser implicitly uses the current protocol (http or https) and treats the next token as the domain. So, /\evil.com effectively becomes https://evil.com.
Let's look at the code. This is a classic example of "naive sanitization"—fixing the input you expect rather than sanitizing the input you actually get. Below is the vulnerable logic compared to the patch.
The Vulnerable Code: It blindly trims forward slashes and prepends one. It assumes the resulting string is safe to serve as a redirect location.
// The middleware receives the request path
path := r.URL.Path
// ... logic to detect if we need to redirect ...
// The Fatal Mistake:
// This removes forward slashes but ignores backslashes
path = "/" + strings.Trim(path, "/")
// The server redirects the user to this "cleaned" path
http.Redirect(w, r, path, 301)The Fix (Commit 6eb3588):
The fix is straightforward but vital. Before we start trimming things, we must normalize the input. By converting all backslashes to forward slashes first, the Trim function works as intended.
// The Fix:
// 1. Normalize all backslashes to forward slashes first
path = strings.ReplaceAll(path, `\`, `/`)
// 2. NOW we can trim. Since backslashes are gone,
// strings.Trim will correctly remove all leading/trailing separators.
path = "/" + strings.Trim(path, "/")
http.Redirect(w, r, path, 301)This simple addition of strings.ReplaceAll kills the exploit dead. Now, path becomes //evil.com/ (normalized), then evil.com (trimmed), then /evil.com (prepended). The browser sees /evil.com, which is a safe, relative path on the origin server. Crisis averted.
So, how do we weaponize this? We aren't dropping shellcode here; we are manipulating trust. The goal is to create a link that looks like it belongs to a legitimate domain but sends the user somewhere else. This is a "Trust-Based" attack.
Imagine a scenario where legit-bank.com uses go-chi for its API routing. An attacker wants to steal credentials. They can't hack the bank, but they can trick the bank into redirecting users to a fake login page.
The Attack Chain:
legit-bank.com strips trailing slashes on its API endpoints. They test https://legit-bank.com/api// and get redirected to /api. This confirms a normalization middleware is likely in play.https://legit-bank.com/\attacker-site.com/login/legit-bank.com. It looks safe. They click.legit-bank.com server receives the request. The RedirectSlashes middleware sees the trailing slash and decides to helpfully "clean" the URL. It transforms /\attacker-site.com/login/ into /\attacker-site.com/login and issues a 301 Redirect./\. It treats this as a network-path reference (protocol relative). It seamlessly navigates the user to https://attacker-site.com/login.Because the initial request was to the real bank, and the redirect happens instantly, many users (and even some security tools) won't notice the switch until they've already typed their password into the fake site.
If you are running go-chi/chi in production, check your go.mod file immediately. The vulnerable versions are typically in the v5.2.x range (specifically 5.2.2 and 5.2.3, though versioning can be tricky with Go modules). The fix landed in version 5.2.4.
Remediation Steps:
go get -u github.com/go-chi/chi/v5@v5.2.4.go list -m all | grep chi).RedirectSlashes middleware. It is a convenience feature, not a security feature. Removing it will stop the redirects (and the exploit), though your users might see 404s if they type trailing slashes.For Developers Writing Custom Middleware:
This is a lesson in "Input Normalization before Validation." Never assume a URL path contains only standard characters. Browsers are incredibly forgiving of malformed URLs, and that forgiveness is exactly what attackers exploit. If you are trimming paths, you must account for \, %5c, and other weird representations of separators.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
github.com/go-chi/chi/v5 go-chi | >= 5.2.2, < 5.2.4 | 5.2.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-601 |
| Attack Vector | Network |
| CVSS Score | 4.7 (Medium) |
| Impact | Open Redirect / Phishing |
| Browser Behavior | Treats '/\' as '//' |
| Patched Version | 5.2.4 |
URL Redirection to Untrusted Site ('Open Redirect')