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/transient" |
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 transient.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 |