-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxsrf.go
130 lines (110 loc) · 2.58 KB
/
xsrf.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package fork
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"crypto/subtle"
"encoding/base64"
"fmt"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
)
type xsrf struct {
Secret string
Key string
current string
*named
Processor
}
func (x *xsrf) New(i ...interface{}) Field {
var newfield xsrf = *x
newfield.named = x.named.Copy()
newfield.current = ""
return &newfield
}
func (x *xsrf) Get() *Value {
return NewValue(x.Token())
}
func (x *xsrf) Set(r *http.Request) {
v := x.Filter(x.Name(), r)
x.current = v.String()
err := x.Validate(x)
if err == nil {
x.current = ""
}
}
func (x *xsrf) Token() string {
if x.current == "" {
return generateTokenAtTime(x.Secret, x.Key, time.Now())
}
return x.current
}
func clean(s string) string {
return strings.Replace(s, ":", "_", -1)
}
func generateTokenAtTime(secret string, key string, now time.Time) string {
h := hmac.New(sha1.New, []byte(key))
fmt.Fprintf(h, "%s:%s:%d", clean(secret), clean(key), now.UnixNano())
tok := fmt.Sprintf("%s:%d", h.Sum(nil), now.UnixNano())
return base64.URLEncoding.EncodeToString([]byte(tok))
}
func ValidateXsrf(x *xsrf) error {
if x.current != "" {
valid := validTokenAtTime(x.current, x.Secret, x.Key, time.Now())
if !valid {
return fmt.Errorf("Invalid XSRF Token")
}
}
return nil
}
const Timeout = 12 * time.Hour
func validTokenAtTime(token string, secret string, key string, now time.Time) bool {
data, err := base64.URLEncoding.DecodeString(token)
if err != nil {
return false
}
sep := bytes.LastIndex(data, []byte{':'})
if sep < 0 {
return false
}
nanos, err := strconv.ParseInt(string(data[sep+1:]), 10, 64)
if err != nil {
return false
}
issueTime := time.Unix(0, nanos)
if now.Sub(issueTime) >= Timeout {
return false
}
if issueTime.After(now.Add(1 * time.Minute)) {
return false
}
expected := generateTokenAtTime(secret, key, issueTime)
return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1
}
const xsrfWidget = `<input type="hidden" name="{{ .Name }}" value="{{ .Token }}" >`
var keybase = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
func randomSequence(n int) string {
rand.Seed(time.Now().UTC().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = keybase[rand.Intn(len(keybase))]
}
return string(b)
}
func XSRF(name string, secret string) Field {
ret := &xsrf{
Secret: secret,
Key: randomSequence(12),
named: newnamed(name),
Processor: NewProcessor(
NewWidget(xsrfWidget),
NewValidater(ValidateXsrf),
NewFilterer(),
),
}
ret.SetValidateable(true)
return ret
}