CVE-2021-21291: URL Redirection to Untrusted Site ('Open Redirect') (CWE-601)
value: OAuth2 Proxy is an open-source reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others) to validate accounts by email, domain or group. In OAuth2 Proxy before version 7.0.0, for users that use the whitelist domain feature, a domain that ended in a similar way to the intended domain could have been allowed as a redirect. For example, if a whitelist domain was configured for ".example.com", the intention is that subdomains of example.com are allowed. Instead, "example.com" and "badexample.com" could also match. This is fixed in version 7.0.0 onwards. As a workaround, one can disable the whitelist domain feature and run separate OAuth2 Proxy instances for each subdomain.
CWE description: 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.
CVSS v3 base score: 6.1
Published: 2021-02-02T19:15Z
Repository: https://github.com/oauth2-proxy/oauth2-proxy
Language: Go
Files changed: oauthproxy.go, oauthproxy_test.go
Commit date: 2021-02-01 18:04:33 +0000
Commit message: Merge pull request from GHSA-4mf2-f3wh-gvf2


Diff: diff --git a/oauthproxy.go b/oauthproxy.go
index 36c58c4..0cfa1f9 100644
--- a/oauthproxy.go
+++ b/oauthproxy.go
@@ -435,25 +435,27 @@ func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
 			logger.Printf("Rejecting invalid redirect %q: scheme unsupported or missing", redirect)
 			return false
 		}
 		redirectHostname := redirectURL.Hostname()
 
-		for _, domain := range p.whitelistDomains {
-			domainHostname, domainPort := splitHostPort(strings.TrimLeft(domain, "."))
-			if domainHostname == "" {
+		for _, allowedDomain := range p.whitelistDomains {
+			allowedHost, allowedPort := splitHostPort(allowedDomain)
+			if allowedHost == "" {
 				continue
 			}
 
-			if (redirectHostname == domainHostname) || (strings.HasPrefix(domain, ".") && strings.HasSuffix(redirectHostname, domainHostname)) {
+			if redirectHostname == strings.TrimPrefix(allowedHost, ".") ||
+				(strings.HasPrefix(allowedHost, ".") &&
+					strings.HasSuffix(redirectHostname, allowedHost)) {
 				// the domain names match, now validate the ports
 				// if the whitelisted domain's port is '*', allow all ports
 				// if the whitelisted domain contains a specific port, only allow that port
 				// if the whitelisted domain doesn't contain a port at all, only allow empty redirect ports ie http and https
 				redirectPort := redirectURL.Port()
-				if (domainPort == "*") ||
-					(domainPort == redirectPort) ||
-					(domainPort == "" && redirectPort == "") {
+				if allowedPort == "*" ||
+					allowedPort == redirectPort ||
+					(allowedPort == "" && redirectPort == "") {
 					return true
 				}
 			}
 		}
 
diff --git a/oauthproxy_test.go b/oauthproxy_test.go
index 41ac81b..06b77ca 100644
--- a/oauthproxy_test.go
+++ b/oauthproxy_test.go
@@ -296,10 +296,15 @@ func TestIsValidRedirect(t *testing.T) {
 		{
 			Desc:           "openRedirectDoubleTab",
 			Redirect:       "/\t/\t\\evil.com",
 			ExpectedResult: false,
 		},
+		{
+			Desc:           "openRedirectPartialSubdomain",
+			Redirect:       "http://evilbar.foo",
+			ExpectedResult: false,
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.Desc, func(t *testing.T) {
 			result := proxy.IsValidRedirect(tc.Redirect)

Vulnerable code: 		for _, domain := range p.whitelistDomains {
			domainHostname, domainPort := splitHostPort(strings.TrimLeft(domain, "."))
			if domainHostname == "" {
			if (redirectHostname == domainHostname) || (strings.HasPrefix(domain, ".") && strings.HasSuffix(redirectHostname, domainHostname)) {
				if (domainPort == "*") ||
					(domainPort == redirectPort) ||
					(domainPort == "" && redirectPort == "") {
Fixed code: 		for _, allowedDomain := range p.whitelistDomains {
			allowedHost, allowedPort := splitHostPort(allowedDomain)
			if allowedHost == "" {
			if redirectHostname == strings.TrimPrefix(allowedHost, ".") ||
				(strings.HasPrefix(allowedHost, ".") &&
					strings.HasSuffix(redirectHostname, allowedHost)) {
				if allowedPort == "*" ||
					allowedPort == redirectPort ||
					(allowedPort == "" && redirectPort == "") {
		{
			Desc:           "openRedirectPartialSubdomain",
			Redirect:       "http://evilbar.foo",
			ExpectedResult: false,
		},