mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-03-19 21:27:58 +02:00
update port whitelisting rules, refactor IsValidRedirect tests
This commit is contained in:
parent
ae4e9155d2
commit
a12bae35ca
@ -494,6 +494,43 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// splitHostPort separates host and port. If the port is not valid, it returns
|
||||||
|
// the entire input as host, and it doesn't check the validity of the host.
|
||||||
|
// Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.
|
||||||
|
// *** taken from net/url, modified validOptionalPort() to accept ":*"
|
||||||
|
func splitHostPort(hostport string) (host, port string) {
|
||||||
|
host = hostport
|
||||||
|
|
||||||
|
colon := strings.LastIndexByte(host, ':')
|
||||||
|
if colon != -1 && validOptionalPort(host[colon:]) {
|
||||||
|
host, port = host[:colon], host[colon+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||||
|
host = host[1 : len(host)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// validOptionalPort reports whether port is either an empty string
|
||||||
|
// or matches /^:\d*$/
|
||||||
|
// *** taken from net/url, modified to accept ":*"
|
||||||
|
func validOptionalPort(port string) bool {
|
||||||
|
if port == "" || port == ":*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if port[0] != ':' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, b := range port[1:] {
|
||||||
|
if b < '0' || b > '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// IsValidRedirect checks whether the redirect URL is whitelisted
|
// IsValidRedirect checks whether the redirect URL is whitelisted
|
||||||
func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
|
func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
|
||||||
switch {
|
switch {
|
||||||
@ -507,19 +544,20 @@ func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
|
|||||||
redirectHostname := redirectURL.Hostname()
|
redirectHostname := redirectURL.Hostname()
|
||||||
|
|
||||||
for _, domain := range p.whitelistDomains {
|
for _, domain := range p.whitelistDomains {
|
||||||
domainURL := url.URL{
|
domainHostname, domainPort := splitHostPort(strings.TrimLeft(domain, "."))
|
||||||
Host: strings.TrimLeft(domain, "."),
|
if err != nil || domainHostname == "" {
|
||||||
}
|
|
||||||
domainHostname := domainURL.Hostname()
|
|
||||||
if domainHostname == "" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirectHostname == domainHostname) || (strings.HasPrefix(domain, ".") && strings.HasSuffix(redirectHostname, domainHostname)) {
|
if (redirectHostname == domainHostname) || (strings.HasPrefix(domain, ".") && strings.HasSuffix(redirectHostname, domainHostname)) {
|
||||||
// if the domain has a port, only allow that port
|
// the domain names match, now validate the ports
|
||||||
// otherwise allow any port
|
// if the whitelisted domain's port is '*', allow all ports
|
||||||
domainPort := domainURL.Port()
|
// if the whitelisted domain contains a specific port, only allow that port
|
||||||
if (domainPort == "") || (domainPort == redirectURL.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 == "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,70 +182,158 @@ func TestIsValidRedirect(t *testing.T) {
|
|||||||
opts.ClientSecret = "fgkdsgj"
|
opts.ClientSecret = "fgkdsgj"
|
||||||
opts.CookieSecret = "ljgiogbj"
|
opts.CookieSecret = "ljgiogbj"
|
||||||
// Should match domains that are exactly foo.bar and any subdomain of bar.foo
|
// Should match domains that are exactly foo.bar and any subdomain of bar.foo
|
||||||
opts.WhitelistDomains = []string{"foo.bar", ".bar.foo", "port.bar:8080", ".sub.port.bar:8080"}
|
opts.WhitelistDomains = []string{
|
||||||
|
"foo.bar",
|
||||||
|
".bar.foo",
|
||||||
|
"port.bar:8080",
|
||||||
|
".sub.port.bar:8080",
|
||||||
|
"anyport.bar:*",
|
||||||
|
".sub.anyport.bar:*",
|
||||||
|
}
|
||||||
opts.Validate()
|
opts.Validate()
|
||||||
|
|
||||||
proxy := NewOAuthProxy(opts, func(string) bool { return true })
|
proxy := NewOAuthProxy(opts, func(string) bool { return true })
|
||||||
|
|
||||||
noRD := proxy.IsValidRedirect("")
|
testCases := []struct {
|
||||||
assert.Equal(t, false, noRD)
|
Desc, Redirect string
|
||||||
|
ExpectedResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Desc: "noRD",
|
||||||
|
Redirect: "",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "singleSlash",
|
||||||
|
Redirect: "/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "doubleSlash",
|
||||||
|
Redirect: "//redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validHTTP",
|
||||||
|
Redirect: "http://foo.bar/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validHTTPS",
|
||||||
|
Redirect: "https://foo.bar/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidHTTPSubdomain",
|
||||||
|
Redirect: "http://baz.foo.bar/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidHTTPSSubdomain",
|
||||||
|
Redirect: "https://baz.foo.bar/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validHTTPSubdomain",
|
||||||
|
Redirect: "http://baz.bar.foo/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validHTTPSSubdomain",
|
||||||
|
Redirect: "https://baz.bar.foo/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validHTTPDomain",
|
||||||
|
Redirect: "http://bar.foo/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidHTTP1",
|
||||||
|
Redirect: "http://foo.bar.evil.corp/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidHTTPS1",
|
||||||
|
Redirect: "https://foo.bar.evil.corp/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidHTTP2",
|
||||||
|
Redirect: "http://evil.corp/redirect?rd=foo.bar",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidHTTPS2",
|
||||||
|
Redirect: "https://evil.corp/redirect?rd=foo.bar",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidPort",
|
||||||
|
Redirect: "https://evil.corp:3838/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidEmptyPort",
|
||||||
|
Redirect: "http://foo.bar:3838/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidEmptyPortSubdomain",
|
||||||
|
Redirect: "http://baz.bar.foo:3838/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validSpecificPort",
|
||||||
|
Redirect: "http://port.bar:8080/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidSpecificPort",
|
||||||
|
Redirect: "http://port.bar:3838/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validSpecificPortSubdomain",
|
||||||
|
Redirect: "http://foo.sub.port.bar:8080/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "invalidSpecificPortSubdomain",
|
||||||
|
Redirect: "http://foo.sub.port.bar:3838/redirect",
|
||||||
|
ExpectedResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validAnyPort1",
|
||||||
|
Redirect: "http://anyport.bar:8080/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validAnyPort2",
|
||||||
|
Redirect: "http://anyport.bar:8081/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validAnyPortSubdomain1",
|
||||||
|
Redirect: "http://a.sub.anyport.bar:8080/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Desc: "validAnyPortSubdomain2",
|
||||||
|
Redirect: "http://a.sub.anyport.bar:8081/redirect",
|
||||||
|
ExpectedResult: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
singleSlash := proxy.IsValidRedirect("/redirect")
|
for _, tc := range testCases {
|
||||||
assert.Equal(t, true, singleSlash)
|
t.Run(tc.Desc, func(t *testing.T) {
|
||||||
|
result := proxy.IsValidRedirect(tc.Redirect)
|
||||||
|
|
||||||
doubleSlash := proxy.IsValidRedirect("//redirect")
|
if result != tc.ExpectedResult {
|
||||||
assert.Equal(t, false, doubleSlash)
|
t.Errorf("expected %t got %t", tc.ExpectedResult, result)
|
||||||
|
}
|
||||||
validHTTP := proxy.IsValidRedirect("http://foo.bar/redirect")
|
})
|
||||||
assert.Equal(t, true, validHTTP)
|
}
|
||||||
|
|
||||||
validHTTPS := proxy.IsValidRedirect("https://foo.bar/redirect")
|
|
||||||
assert.Equal(t, true, validHTTPS)
|
|
||||||
|
|
||||||
invalidHTTPSubdomain := proxy.IsValidRedirect("http://baz.foo.bar/redirect")
|
|
||||||
assert.Equal(t, false, invalidHTTPSubdomain)
|
|
||||||
|
|
||||||
invalidHTTPSSubdomain := proxy.IsValidRedirect("https://baz.foo.bar/redirect")
|
|
||||||
assert.Equal(t, false, invalidHTTPSSubdomain)
|
|
||||||
|
|
||||||
validHTTPSubdomain := proxy.IsValidRedirect("http://baz.bar.foo/redirect")
|
|
||||||
assert.Equal(t, true, validHTTPSubdomain)
|
|
||||||
|
|
||||||
validHTTPSSubdomain := proxy.IsValidRedirect("https://baz.bar.foo/redirect")
|
|
||||||
assert.Equal(t, true, validHTTPSSubdomain)
|
|
||||||
|
|
||||||
invalidHTTP1 := proxy.IsValidRedirect("http://foo.bar.evil.corp/redirect")
|
|
||||||
assert.Equal(t, false, invalidHTTP1)
|
|
||||||
|
|
||||||
invalidHTTPS1 := proxy.IsValidRedirect("https://foo.bar.evil.corp/redirect")
|
|
||||||
assert.Equal(t, false, invalidHTTPS1)
|
|
||||||
|
|
||||||
invalidHTTP2 := proxy.IsValidRedirect("http://evil.corp/redirect?rd=foo.bar")
|
|
||||||
assert.Equal(t, false, invalidHTTP2)
|
|
||||||
|
|
||||||
invalidHTTPS2 := proxy.IsValidRedirect("https://evil.corp/redirect?rd=foo.bar")
|
|
||||||
assert.Equal(t, false, invalidHTTPS2)
|
|
||||||
|
|
||||||
invalidPort := proxy.IsValidRedirect("https://evil.corp:3838/redirect")
|
|
||||||
assert.Equal(t, false, invalidPort)
|
|
||||||
|
|
||||||
validAnyPort := proxy.IsValidRedirect("http://foo.bar:3838/redirect")
|
|
||||||
assert.Equal(t, true, validAnyPort)
|
|
||||||
|
|
||||||
validAnyPortSubdomain := proxy.IsValidRedirect("http://baz.bar.foo:3838/redirect")
|
|
||||||
assert.Equal(t, true, validAnyPortSubdomain)
|
|
||||||
|
|
||||||
validSpecificPort := proxy.IsValidRedirect("http://port.bar:8080/redirect")
|
|
||||||
assert.Equal(t, true, validSpecificPort)
|
|
||||||
|
|
||||||
invalidSpecificPort := proxy.IsValidRedirect("http://port.bar:3838/redirect")
|
|
||||||
assert.Equal(t, false, invalidSpecificPort)
|
|
||||||
|
|
||||||
validSpecificPortSubdomain := proxy.IsValidRedirect("http://foo.sub.port.bar:8080/redirect")
|
|
||||||
assert.Equal(t, true, validSpecificPortSubdomain)
|
|
||||||
|
|
||||||
invalidSpecificPortSubdomain := proxy.IsValidRedirect("http://foo.sub.port.bar:3838/redirect")
|
|
||||||
assert.Equal(t, false, invalidSpecificPortSubdomain)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestProvider struct {
|
type TestProvider struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user