| 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 "github.com/julienschmidt/httprouter" | |
| 20 "golang.org/x/net/context" | 19 "golang.org/x/net/context" |
| 21 | 20 |
| 22 "github.com/luci/luci-go/common/errors" | 21 "github.com/luci/luci-go/common/errors" |
| 23 "github.com/luci/luci-go/common/logging" | 22 "github.com/luci/luci-go/common/logging" |
| 24 | 23 |
| 25 "github.com/luci/luci-go/server/auth" | 24 "github.com/luci/luci-go/server/auth" |
| 26 » "github.com/luci/luci-go/server/middleware" | 25 » "github.com/luci/luci-go/server/router" |
| 27 "github.com/luci/luci-go/server/tokens" | 26 "github.com/luci/luci-go/server/tokens" |
| 28 ) | 27 ) |
| 29 | 28 |
| 30 // xsrfToken described how to generate tokens. | 29 // xsrfToken described how to generate tokens. |
| 31 var xsrfToken = tokens.TokenKind{ | 30 var xsrfToken = tokens.TokenKind{ |
| 32 Algo: tokens.TokenAlgoHmacSHA256, | 31 Algo: tokens.TokenAlgoHmacSHA256, |
| 33 Expiration: 4 * time.Hour, | 32 Expiration: 4 * time.Hour, |
| 34 SecretKey: "xsrf_token", | 33 SecretKey: "xsrf_token", |
| 35 Version: 1, | 34 Version: 1, |
| 36 } | 35 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 63 panic(err) | 62 panic(err) |
| 64 } | 63 } |
| 65 return template.HTML(fmt.Sprintf(`<input type="hidden" name="xsrf_token"
value="%s">`, tok)) | 64 return template.HTML(fmt.Sprintf(`<input type="hidden" name="xsrf_token"
value="%s">`, tok)) |
| 66 } | 65 } |
| 67 | 66 |
| 68 // WithTokenCheck is middleware that checks validity of XSRF tokens. | 67 // WithTokenCheck is middleware that checks validity of XSRF tokens. |
| 69 // | 68 // |
| 70 // 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 |
| 71 // 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 |
| 72 // invalid. | 71 // invalid. |
| 73 func WithTokenCheck(h middleware.Handler) middleware.Handler { | 72 func WithTokenCheck(c *router.Context, next router.Handler) { |
| 74 » return func(c context.Context, rw http.ResponseWriter, r *http.Request,
p httprouter.Params) { | 73 » tok := c.Request.PostFormValue("xsrf_token") |
| 75 » » tok := r.PostFormValue("xsrf_token") | 74 » if tok == "" { |
| 76 » » if tok == "" { | 75 » » replyError(c.Context, c.Writer, http.StatusForbidden, "XSRF toke
n is missing") |
| 77 » » » replyError(c, rw, http.StatusForbidden, "XSRF token is m
issing") | 76 » » return |
| 78 » » » return | 77 » } |
| 79 » » } | 78 » switch err := Check(c.Context, tok); { |
| 80 » » switch err := Check(c, tok); { | 79 » case errors.IsTransient(err): |
| 81 » » case errors.IsTransient(err): | 80 » » replyError(c.Context, c.Writer, http.StatusInternalServerError,
"Transient error when checking XSRF token - %s", err) |
| 82 » » » replyError(c, rw, http.StatusInternalServerError, "Trans
ient error when checking XSRF token - %s", err) | 81 » case err != nil: |
| 83 » » case err != nil: | 82 » » replyError(c.Context, c.Writer, http.StatusForbidden, "Bad XSRF
token - %s", err) |
| 84 » » » replyError(c, rw, http.StatusForbidden, "Bad XSRF token
- %s", err) | 83 » default: |
| 85 » » default: | 84 » » next(c) |
| 86 » » » h(c, rw, r, p) | |
| 87 » » } | |
| 88 } | 85 } |
| 89 } | 86 } |
| 90 | 87 |
| 91 /// | 88 /// |
| 92 | 89 |
| 93 // 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 |
| 94 // the verification to succeed. | 91 // the verification to succeed. |
| 95 func state(c context.Context) []byte { | 92 func state(c context.Context) []byte { |
| 96 return []byte(auth.CurrentUser(c).Identity) | 93 return []byte(auth.CurrentUser(c).Identity) |
| 97 } | 94 } |
| 98 | 95 |
| 99 // replyError sends error response and logs it. | 96 // replyError sends error response and logs it. |
| 100 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{}) { |
| 101 text := fmt.Sprintf(msg, args...) | 98 text := fmt.Sprintf(msg, args...) |
| 102 logging.Errorf(c, "xsrf: %s", text) | 99 logging.Errorf(c, "xsrf: %s", text) |
| 103 http.Error(rw, text, code) | 100 http.Error(rw, text, code) |
| 104 } | 101 } |
| OLD | NEW |