| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Package xsrf provides Cross Site Request Forgery prevention middleware. | 5 // Package xsrf provides Cross Site Request Forgery prevention middleware. |
| 6 // | 6 // |
| 7 // Usage: | 7 // Usage: |
| 8 // 1. When serving GET request put hidden "xsrf_token" input field with | 8 // 1. When serving GET request put hidden "xsrf_token" input field with |
| 9 // the token value into the form. Use TokenField(...) to generate it. | 9 // the token value into the form. Use TokenField(...) to generate it. |
| 10 // 2. Wrap POST-handling route with WithTokenCheck(...) middleware. | 10 // 2. Wrap POST-handling route with WithTokenCheck(...) middleware. |
| 11 package xsrf | 11 package xsrf |
| 12 | 12 |
| 13 import ( | 13 import ( |
| 14 "fmt" | 14 "fmt" |
| 15 "html/template" | 15 "html/template" |
| 16 "net/http" | 16 "net/http" |
| 17 "time" | 17 "time" |
| 18 | 18 |
| 19 "golang.org/x/net/context" | 19 "golang.org/x/net/context" |
| 20 | 20 |
| 21 "github.com/luci/luci-go/common/errors" | |
| 22 "github.com/luci/luci-go/common/logging" | 21 "github.com/luci/luci-go/common/logging" |
| 22 "github.com/luci/luci-go/common/retry" |
| 23 | 23 |
| 24 "github.com/luci/luci-go/server/auth" | 24 "github.com/luci/luci-go/server/auth" |
| 25 "github.com/luci/luci-go/server/router" | 25 "github.com/luci/luci-go/server/router" |
| 26 "github.com/luci/luci-go/server/tokens" | 26 "github.com/luci/luci-go/server/tokens" |
| 27 ) | 27 ) |
| 28 | 28 |
| 29 // xsrfToken described how to generate tokens. | 29 // xsrfToken described how to generate tokens. |
| 30 var xsrfToken = tokens.TokenKind{ | 30 var xsrfToken = tokens.TokenKind{ |
| 31 Algo: tokens.TokenAlgoHmacSHA256, | 31 Algo: tokens.TokenAlgoHmacSHA256, |
| 32 Expiration: 4 * time.Hour, | 32 Expiration: 4 * time.Hour, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 // If searches for the token in "xsrf_token" POST form field (as generated by | 69 // If searches for the token in "xsrf_token" POST form field (as generated by |
| 70 // TokenField). Aborts the request with HTTP 403 if XSRF token is missing or | 70 // TokenField). Aborts the request with HTTP 403 if XSRF token is missing or |
| 71 // invalid. | 71 // invalid. |
| 72 func WithTokenCheck(c *router.Context, next router.Handler) { | 72 func WithTokenCheck(c *router.Context, next router.Handler) { |
| 73 tok := c.Request.PostFormValue("xsrf_token") | 73 tok := c.Request.PostFormValue("xsrf_token") |
| 74 if tok == "" { | 74 if tok == "" { |
| 75 replyError(c.Context, c.Writer, http.StatusForbidden, "XSRF toke
n is missing") | 75 replyError(c.Context, c.Writer, http.StatusForbidden, "XSRF toke
n is missing") |
| 76 return | 76 return |
| 77 } | 77 } |
| 78 switch err := Check(c.Context, tok); { | 78 switch err := Check(c.Context, tok); { |
| 79 » case errors.IsTransient(err): | 79 » case retry.Tag.In(err): |
| 80 replyError(c.Context, c.Writer, http.StatusInternalServerError,
"Transient error when checking XSRF token - %s", err) | 80 replyError(c.Context, c.Writer, http.StatusInternalServerError,
"Transient error when checking XSRF token - %s", err) |
| 81 case err != nil: | 81 case err != nil: |
| 82 replyError(c.Context, c.Writer, http.StatusForbidden, "Bad XSRF
token - %s", err) | 82 replyError(c.Context, c.Writer, http.StatusForbidden, "Bad XSRF
token - %s", err) |
| 83 default: | 83 default: |
| 84 next(c) | 84 next(c) |
| 85 } | 85 } |
| 86 } | 86 } |
| 87 | 87 |
| 88 /// | 88 /// |
| 89 | 89 |
| 90 // state must return exact same value when generating and verifying token for | 90 // state must return exact same value when generating and verifying token for |
| 91 // the verification to succeed. | 91 // the verification to succeed. |
| 92 func state(c context.Context) []byte { | 92 func state(c context.Context) []byte { |
| 93 return []byte(auth.CurrentUser(c).Identity) | 93 return []byte(auth.CurrentUser(c).Identity) |
| 94 } | 94 } |
| 95 | 95 |
| 96 // replyError sends error response and logs it. | 96 // replyError sends error response and logs it. |
| 97 func replyError(c context.Context, rw http.ResponseWriter, code int, msg string,
args ...interface{}) { | 97 func replyError(c context.Context, rw http.ResponseWriter, code int, msg string,
args ...interface{}) { |
| 98 text := fmt.Sprintf(msg, args...) | 98 text := fmt.Sprintf(msg, args...) |
| 99 logging.Errorf(c, "xsrf: %s", text) | 99 logging.Errorf(c, "xsrf: %s", text) |
| 100 http.Error(rw, text, code) | 100 http.Error(rw, text, code) |
| 101 } | 101 } |
| OLD | NEW |