Browse Source

routes: fix open redirect vulnerability (#5355)

Reported by @cezar97.
pull/5446/head
Unknwon 6 years ago
parent
commit
bd7d1e2f16
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
  1. 2
      gogs.go
  2. 12
      pkg/tool/path.go
  3. 32
      pkg/tool/path_test.go
  4. 3
      routes/repo/branch.go
  5. 6
      routes/repo/repo.go
  6. 12
      routes/user/auth.go
  7. 5
      routes/user/profile.go
  8. 2
      templates/.VERSION

2
gogs.go

@ -16,7 +16,7 @@ import (
"github.com/gogs/gogs/pkg/setting" "github.com/gogs/gogs/pkg/setting"
) )
const APP_VER = "0.11.66.0928" const APP_VER = "0.11.67.0928"
func init() { func init() {
setting.AppVer = APP_VER setting.AppVer = APP_VER

12
pkg/tool/path.go

@ -0,0 +1,12 @@
// Copyright 2018 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package tool
// IsSameSiteURLPath returns true if the URL path belongs to the same site, false otherwise.
// False: //url, http://url, /\url
// True: /url
func IsSameSiteURLPath(url string) bool {
return len(url) >= 2 && url[0] == '/' && url[1] != '/' && url[1] != '\\'
}

32
pkg/tool/path_test.go

@ -0,0 +1,32 @@
// Copyright 2018 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package tool
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_IsSameSiteURLPath(t *testing.T) {
Convey("Check if a path belongs to the same site", t, func() {
testCases := []struct {
url string
expect bool
}{
{"//github.com", false},
{"http://github.com", false},
{"https://github.com", false},
{"/\\github.com", false},
{"/admin", true},
{"/user/repo", true},
}
for _, tc := range testCases {
So(IsSameSiteURLPath(tc.url), ShouldEqual, tc.expect)
}
})
}

3
routes/repo/branch.go

@ -14,6 +14,7 @@ import (
"github.com/gogs/gogs/models" "github.com/gogs/gogs/models"
"github.com/gogs/gogs/pkg/context" "github.com/gogs/gogs/pkg/context"
"github.com/gogs/gogs/pkg/tool"
) )
const ( const (
@ -112,7 +113,7 @@ func DeleteBranchPost(c *context.Context) {
defer func() { defer func() {
redirectTo := c.Query("redirect_to") redirectTo := c.Query("redirect_to")
if len(redirectTo) == 0 { if !tool.IsSameSiteURLPath(redirectTo) {
redirectTo = c.Repo.RepoLink redirectTo = c.Repo.RepoLink
} }
c.Redirect(redirectTo) c.Redirect(redirectTo)

6
routes/repo/repo.go

@ -242,7 +242,7 @@ func Action(c *context.Context) {
err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, false) err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, false)
case "desc": // FIXME: this is not used case "desc": // FIXME: this is not used
if !c.Repo.IsOwner() { if !c.Repo.IsOwner() {
c.Error(404) c.NotFound()
return return
} }
@ -252,12 +252,12 @@ func Action(c *context.Context) {
} }
if err != nil { if err != nil {
c.Handle(500, fmt.Sprintf("Action (%s)", c.Params(":action")), err) c.ServerError(fmt.Sprintf("Action (%s)", c.Params(":action")), err)
return return
} }
redirectTo := c.Query("redirect_to") redirectTo := c.Query("redirect_to")
if len(redirectTo) == 0 { if !tool.IsSameSiteURLPath(redirectTo) {
redirectTo = c.Repo.RepoLink redirectTo = c.Repo.RepoLink
} }
c.Redirect(redirectTo) c.Redirect(redirectTo)

12
routes/user/auth.go

@ -17,6 +17,7 @@ import (
"github.com/gogs/gogs/pkg/form" "github.com/gogs/gogs/pkg/form"
"github.com/gogs/gogs/pkg/mailer" "github.com/gogs/gogs/pkg/mailer"
"github.com/gogs/gogs/pkg/setting" "github.com/gogs/gogs/pkg/setting"
"github.com/gogs/gogs/pkg/tool"
) )
const ( const (
@ -72,13 +73,6 @@ func AutoLogin(c *context.Context) (bool, error) {
return true, nil return true, nil
} }
// isValidRedirect returns false if the URL does not redirect to same site.
// False: //url, http://url, /\url
// True: /url
func isValidRedirect(url string) bool {
return len(url) >= 2 && url[0] == '/' && url[1] != '/' && url[1] != '\\'
}
func Login(c *context.Context) { func Login(c *context.Context) {
c.Title("sign_in") c.Title("sign_in")
@ -97,7 +91,7 @@ func Login(c *context.Context) {
} }
if isSucceed { if isSucceed {
if isValidRedirect(redirectTo) { if tool.IsSameSiteURLPath(redirectTo) {
c.Redirect(redirectTo) c.Redirect(redirectTo)
} else { } else {
c.SubURLRedirect("/") c.SubURLRedirect("/")
@ -143,7 +137,7 @@ func afterLogin(c *context.Context, u *models.User, remember bool) {
redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")) redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to"))
c.SetCookie("redirect_to", "", -1, setting.AppSubURL) c.SetCookie("redirect_to", "", -1, setting.AppSubURL)
if isValidRedirect(redirectTo) { if tool.IsSameSiteURLPath(redirectTo) {
c.Redirect(redirectTo) c.Redirect(redirectTo)
return return
} }

5
routes/user/profile.go

@ -15,6 +15,7 @@ import (
"github.com/gogs/gogs/models/errors" "github.com/gogs/gogs/models/errors"
"github.com/gogs/gogs/pkg/context" "github.com/gogs/gogs/pkg/context"
"github.com/gogs/gogs/pkg/setting" "github.com/gogs/gogs/pkg/setting"
"github.com/gogs/gogs/pkg/tool"
"github.com/gogs/gogs/routes/repo" "github.com/gogs/gogs/routes/repo"
) )
@ -157,12 +158,12 @@ func Action(c *context.Context) {
} }
if err != nil { if err != nil {
c.Handle(500, fmt.Sprintf("Action (%s)", c.Params(":action")), err) c.ServerError(fmt.Sprintf("Action (%s)", c.Params(":action")), err)
return return
} }
redirectTo := c.Query("redirect_to") redirectTo := c.Query("redirect_to")
if len(redirectTo) == 0 { if !tool.IsSameSiteURLPath(redirectTo) {
redirectTo = u.HomeLink() redirectTo = u.HomeLink()
} }
c.Redirect(redirectTo) c.Redirect(redirectTo)

2
templates/.VERSION

@ -1 +1 @@
0.11.66.0928 0.11.67.0928

Loading…
Cancel
Save