From 7096085f2b07246315e95e394b180ce9729efbb0 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 4 Nov 2018 01:15:55 +0000 Subject: [PATCH] Fix #5226 by adding CSRF checking to api reqToken and add CSRF to the POST header for deadline (#5250) * Add CSRF checking to reqToken and place CSRF in the post for deadline creation Fixes #5226, #5249 * /api/v1/admin/users routes should have reqToken middleware --- integrations/api_admin_test.go | 10 +++++----- integrations/git_test.go | 3 ++- modules/context/api.go | 13 +++++++++++++ public/js/index.js | 4 ++++ routers/api/v1/api.go | 12 ++++++++---- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go index 690edad75..b8dded9c1 100644 --- a/integrations/api_admin_test.go +++ b/integrations/api_admin_test.go @@ -39,8 +39,8 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { OwnerID: keyOwner.ID, }) - req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token="+token, - keyOwner.Name, newPublicKey.ID) + req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s", + keyOwner.Name, newPublicKey.ID, token) session.MakeRequest(t, req, http.StatusNoContent) models.AssertNotExistsBean(t, &models.PublicKey{ID: newPublicKey.ID}) } @@ -51,7 +51,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { session := loginUser(t, "user1") token := getTokenForLoggedInUser(t, session) - req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token="+token, models.NonexistentID) + req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", models.NonexistentID, token) session.MakeRequest(t, req, http.StatusNotFound) } @@ -73,8 +73,8 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { session = loginUser(t, normalUsername) token = getTokenForLoggedInUser(t, session) - req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token="+token, - adminUsername, newPublicKey.ID) + req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s", + adminUsername, newPublicKey.ID, token) session.MakeRequest(t, req, http.StatusForbidden) } diff --git a/integrations/git_test.go b/integrations/git_test.go index 7ac375dd0..96d39e051 100644 --- a/integrations/git_test.go +++ b/integrations/git_test.go @@ -143,7 +143,8 @@ func TestGit(t *testing.T) { session := loginUser(t, "user1") keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User) - urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", keyOwner.Name) + token := getTokenForLoggedInUser(t, session) + urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) dataPubKey, err := ioutil.ReadFile(keyFile + ".pub") assert.NoError(t, err) diff --git a/modules/context/api.go b/modules/context/api.go index 0bf430772..6a9c79237 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -8,6 +8,8 @@ import ( "fmt" "strings" + "github.com/go-macaron/csrf" + "code.gitea.io/git" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" @@ -97,6 +99,17 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) { } } +// RequireCSRF requires a validated a CSRF token +func (ctx *APIContext) RequireCSRF() { + headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName()) + formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName()) + if len(headerToken) > 0 || len(formValueToken) > 0 { + csrf.Validate(ctx.Context.Context, ctx.csrf) + } else { + ctx.Context.Error(401) + } +} + // APIContexter returns apicontext as macaron middleware func APIContexter() macaron.Handler { return func(c *Context) { diff --git a/public/js/index.js b/public/js/index.js index 9aafa7d64..f5d3ef2d9 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -2595,6 +2595,10 @@ function updateDeadline(deadlineString) { data: JSON.stringify({ 'due_date': realDeadline, }), + headers: { + 'X-Csrf-Token': csrf, + 'X-Remote': true, + }, contentType: 'application/json', type: 'POST', success: function () { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b12cb1374..a839ce8dc 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -174,11 +174,15 @@ func repoAssignment() macaron.Handler { // Contexter middleware already checks token for user sign in process. func reqToken() macaron.Handler { - return func(ctx *context.Context) { - if true != ctx.Data["IsApiToken"] { - ctx.Error(401) + return func(ctx *context.APIContext) { + if true == ctx.Data["IsApiToken"] { return } + if ctx.IsSigned { + ctx.RequireCSRF() + return + } + ctx.Context.Error(401) } } @@ -635,7 +639,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) }) }) - }, reqAdmin()) + }, reqToken(), reqAdmin()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch)