Replace CSRF cookie with CrossOriginProtection (#36183)
Removes the CSRF cookie in favor of [`CrossOriginProtection`](https://pkg.go.dev/net/http#CrossOriginProtection) which relies purely on HTTP headers. Fixes: https://github.com/go-gitea/gitea/issues/11188 Fixes: https://github.com/go-gitea/gitea/issues/30333 Helps: https://github.com/go-gitea/gitea/issues/35107 TODOs: - [x] Fix tests - [ ] Ideally add tests to validates the protection --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -38,8 +38,8 @@ func AuthShared(ctx *context.Base, sessionStore auth_service.SessionStore, authM
|
||||
|
||||
// VerifyOptions contains required or check options
|
||||
type VerifyOptions struct {
|
||||
SignInRequired bool
|
||||
SignOutRequired bool
|
||||
AdminRequired bool
|
||||
DisableCSRF bool
|
||||
SignInRequired bool
|
||||
SignOutRequired bool
|
||||
AdminRequired bool
|
||||
DisableCrossOriginProtection bool
|
||||
}
|
||||
|
||||
@@ -102,7 +102,6 @@ func autoSignIn(ctx *context.Context) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ctx.Csrf.PrepareForSessionUser(ctx)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -357,9 +356,6 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
|
||||
}
|
||||
|
||||
// force to generate a new CSRF token
|
||||
ctx.Csrf.PrepareForSessionUser(ctx)
|
||||
|
||||
// Register last login
|
||||
if err := user_service.UpdateUser(ctx, u, &user_service.UpdateOptions{SetLastLogin: true}); err != nil {
|
||||
ctx.ServerError("UpdateUser", err)
|
||||
@@ -403,7 +399,6 @@ func HandleSignOut(ctx *context.Context) {
|
||||
_ = ctx.Session.Flush()
|
||||
_ = ctx.Session.Destroy(ctx.Resp, ctx.Req)
|
||||
ctx.DeleteSiteCookie(setting.CookieRememberName)
|
||||
ctx.Csrf.DeleteCookie(ctx)
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
}
|
||||
|
||||
@@ -811,8 +806,6 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Csrf.PrepareForSessionUser(ctx)
|
||||
|
||||
if err := resetLocale(ctx, user); err != nil {
|
||||
ctx.ServerError("resetLocale", err)
|
||||
return
|
||||
|
||||
@@ -393,9 +393,6 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m
|
||||
return
|
||||
}
|
||||
|
||||
// force to generate a new CSRF token
|
||||
ctx.Csrf.PrepareForSessionUser(ctx)
|
||||
|
||||
if err := resetLocale(ctx, u); err != nil {
|
||||
ctx.ServerError("resetLocale", err)
|
||||
return
|
||||
|
||||
@@ -22,5 +22,5 @@ func addOwnerRepoGitHTTPRouters(m *web.Router) {
|
||||
m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject)
|
||||
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile)
|
||||
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile)
|
||||
}, optSignInIgnoreCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb())
|
||||
}, repo.HTTPGitEnabledHandler, repo.CorsHandler(), optSignInFromAnyOrigin, context.UserAssignmentWeb())
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ func loadKeysData(ctx *context.Context) {
|
||||
ctx.Data["GPGKeys"] = gpgkeys
|
||||
tokenToSign := asymkey_model.VerificationToken(ctx.Doer, 1)
|
||||
|
||||
// generate a new aes cipher using the csrfToken
|
||||
// generate a new aes cipher using the token
|
||||
ctx.Data["TokenToSign"] = tokenToSign
|
||||
|
||||
principals, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
|
||||
|
||||
+20
-13
@@ -129,13 +129,13 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
|
||||
// ensure the session uid is deleted
|
||||
_ = ctx.Session.Delete("uid")
|
||||
}
|
||||
|
||||
ctx.Csrf.PrepareForSessionUser(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// verifyAuthWithOptions checks authentication according to options
|
||||
func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Context) {
|
||||
crossOriginProtection := http.NewCrossOriginProtection()
|
||||
|
||||
return func(ctx *context.Context) {
|
||||
// Check prohibit login users.
|
||||
if ctx.IsSigned {
|
||||
@@ -178,9 +178,9 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
|
||||
return
|
||||
}
|
||||
|
||||
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == http.MethodPost {
|
||||
ctx.Csrf.Validate(ctx)
|
||||
if ctx.Written() {
|
||||
if !options.SignOutRequired && !options.DisableCrossOriginProtection {
|
||||
if err := crossOriginProtection.Check(ctx.Req); err != nil {
|
||||
http.Error(ctx.Resp, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -292,7 +292,12 @@ func Routes() *web.Router {
|
||||
return routes
|
||||
}
|
||||
|
||||
var optSignInIgnoreCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true})
|
||||
// optSignInFromAnyOrigin means that the user can (optionally) be signed in from any origin (no cross-origin protection)
|
||||
// - With CORS middleware: CORS middleware does the preflight request handling, the requests has Sec-Fetch-Site header.
|
||||
// The CORS mechanism already protects cross-origin requests, and the CrossOriginProtection has no "allowed origin" list, so disable CrossOriginProtection.
|
||||
// - For non-browser client requests: git clone via http, no Sec-Fetch-Site header.
|
||||
// Such requests are not cross-origin requests, so disable CrossOriginProtection.
|
||||
var optSignInFromAnyOrigin = verifyAuthWithOptions(&common.VerifyOptions{DisableCrossOriginProtection: true})
|
||||
|
||||
// registerWebRoutes register routes
|
||||
func registerWebRoutes(m *web.Router) {
|
||||
@@ -489,7 +494,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/-/markup", reqSignIn, web.Bind(structs.MarkupOption{}), misc.Markup)
|
||||
|
||||
m.Get("/-/web-theme/list", misc.WebThemeList)
|
||||
m.Post("/-/web-theme/apply", optSignInIgnoreCsrf, misc.WebThemeApply)
|
||||
m.Post("/-/web-theme/apply", optSignIn, misc.WebThemeApply)
|
||||
|
||||
m.Group("/explore", func() {
|
||||
m.Get("", func(ctx *context.Context) {
|
||||
@@ -565,12 +570,14 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/grant", web.Bind(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth)
|
||||
// TODO manage redirection
|
||||
m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth)
|
||||
}, optSignInIgnoreCsrf, reqSignIn)
|
||||
}, reqSignIn)
|
||||
|
||||
m.Methods("GET, POST, OPTIONS", "/userinfo", optionsCorsHandler(), optSignInIgnoreCsrf, auth.InfoOAuth)
|
||||
m.Methods("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), optSignInIgnoreCsrf, auth.AccessTokenOAuth)
|
||||
m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), optSignInIgnoreCsrf, auth.OIDCKeys)
|
||||
m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), optSignInIgnoreCsrf, auth.IntrospectOAuth)
|
||||
m.Group("", func() {
|
||||
m.Methods("GET, POST, OPTIONS", "/userinfo", auth.InfoOAuth)
|
||||
m.Methods("POST, OPTIONS", "/access_token", web.Bind(forms.AccessTokenForm{}), auth.AccessTokenOAuth)
|
||||
m.Methods("GET, OPTIONS", "/keys", auth.OIDCKeys)
|
||||
m.Methods("POST, OPTIONS", "/introspect", web.Bind(forms.IntrospectTokenForm{}), auth.IntrospectOAuth)
|
||||
}, optionsCorsHandler(), optSignInFromAnyOrigin)
|
||||
}, oauth2Enabled)
|
||||
|
||||
m.Group("/user/settings", func() {
|
||||
@@ -1653,7 +1660,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/action/{action:accept_transfer|reject_transfer}", reqSignIn, repo.ActionTransfer)
|
||||
}, optSignIn, context.RepoAssignment)
|
||||
|
||||
common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support
|
||||
common.AddOwnerRepoGitLFSRoutes(m, lfsServerEnabled, repo.CorsHandler(), optSignInFromAnyOrigin) // "/{username}/{reponame}/{lfs-paths}": git-lfs support, see also addOwnerRepoGitHTTPRouters
|
||||
|
||||
addOwnerRepoGitHTTPRouters(m) // "/{username}/{reponame}/{git-paths}": git http support
|
||||
|
||||
|
||||
Reference in New Issue
Block a user