[release-11.6.8] Stricter validation for redirect URLs (#113863)

Stricter validation for redirect URLs (#113852)

(cherry picked from commit 3f48a6358f)
This commit is contained in:
grafana-delivery-bot[bot]
2025-11-13 18:48:43 +01:00
committed by GitHub
parent 40fc660cc8
commit d4fcd25632
2 changed files with 13 additions and 26 deletions
+6 -20
View File
@@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"path"
"regexp" "regexp"
"strings" "strings"
@@ -41,8 +40,10 @@ var getViewIndex = func() string {
return viewIndex return viewIndex
} }
// Only allow redirects that start with a slash followed by an alphanumerical character, a dash or an underscore. var redirectAllowRe = regexp.MustCompile(`^/[a-zA-Z0-9-_./]*$`)
var redirectRe = regexp.MustCompile(`^/[a-zA-Z0-9-_].*`)
// Do not allow redirect URLs that contain "//" or ".."
var redirectDenyRe = regexp.MustCompile(`(//|\.\.)`)
var ( var (
errAbsoluteRedirectTo = errors.New("absolute URLs are not allowed for redirect_to cookie value") errAbsoluteRedirectTo = errors.New("absolute URLs are not allowed for redirect_to cookie value")
@@ -64,26 +65,11 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
return errForbiddenRedirectTo return errForbiddenRedirectTo
} }
// path should have exactly one leading slash if redirectDenyRe.MatchString(to.Path) {
if !strings.HasPrefix(to.Path, "/") {
return errForbiddenRedirectTo return errForbiddenRedirectTo
} }
if strings.HasPrefix(to.Path, "//") { if to.Path != "/" && !redirectAllowRe.MatchString(to.Path) {
return errForbiddenRedirectTo
}
if to.Path != "/" && !redirectRe.MatchString(to.Path) {
return errForbiddenRedirectTo
}
cleanPath := path.Clean(to.Path)
// "." is what path.Clean returns for empty paths
if cleanPath == "." {
return errForbiddenRedirectTo
}
if cleanPath != "/" && !redirectRe.MatchString(cleanPath) {
return errForbiddenRedirectTo return errForbiddenRedirectTo
} }
+7 -6
View File
@@ -3,7 +3,6 @@ package middleware
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"path"
"regexp" "regexp"
"strconv" "strconv"
@@ -13,8 +12,10 @@ import (
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
// Only allow redirects that start with a slash followed by an alphanumerical character, a dash or an underscore. var redirectAllowRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_./]*$`)
var redirectRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_].*`)
// Do not allow redirect URLs that contain "//" or ".."
var redirectDenyRe = regexp.MustCompile(`(//|\.\.)`)
// OrgRedirect changes org and redirects users if the // OrgRedirect changes org and redirects users if the
// querystring `orgId` doesn't match the active org. // querystring `orgId` doesn't match the active org.
@@ -66,9 +67,9 @@ func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
} }
func validRedirectPath(p string) bool { func validRedirectPath(p string) bool {
if p != "" && p != "/" && !redirectRe.MatchString(p) { if redirectDenyRe.MatchString(p) {
return false return false
} }
cleanPath := path.Clean(p)
return cleanPath == "." || cleanPath == "/" || redirectRe.MatchString(cleanPath) return p == "" || p == "/" || redirectAllowRe.MatchString(p)
} }