github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/third_party/code.google.com/p/xsrftoken/xsrf.go (about) 1 // Copyright 2012 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package xsrftoken provides methods for generating and validating secure XSRF tokens. 16 package xsrftoken 17 18 import ( 19 "bytes" 20 "crypto/hmac" 21 "crypto/sha1" 22 "encoding/base64" 23 "fmt" 24 "strconv" 25 "strings" 26 "time" 27 ) 28 29 // The duration that XSRF tokens are valid. 30 // It is exported so clients may set cookie timeouts that match generated tokens. 31 const Timeout = 24 * time.Hour 32 33 // clean sanitizes a string for inclusion in a token by replacing all ":"s. 34 func clean(s string) string { 35 return strings.Replace(s, ":", "_", -1) 36 } 37 38 // Generate returns a URL-safe secure XSRF token that expires in 24 hours. 39 // 40 // key is a secret key for your application. 41 // userID is a unique identifier for the user. 42 // actionID is the action the user is taking (e.g. POSTing to a particular path). 43 func Generate(key, userID, actionID string) string { 44 return generateAtTime(key, userID, actionID, time.Now()) 45 } 46 47 // generateAtTime is like Generate, but returns a token that expires 24 hours from now. 48 func generateAtTime(key, userID, actionID string, now time.Time) string { 49 h := hmac.New(sha1.New, []byte(key)) 50 fmt.Fprintf(h, "%s:%s:%d", clean(userID), clean(actionID), now.UnixNano()) 51 tok := fmt.Sprintf("%s:%d", h.Sum(nil), now.UnixNano()) 52 return base64.URLEncoding.EncodeToString([]byte(tok)) 53 } 54 55 // Valid returns true if token is a valid, unexpired token returned by Generate. 56 func Valid(token, key, userID, actionID string) bool { 57 return validAtTime(token, key, userID, actionID, time.Now()) 58 } 59 60 // validAtTime is like Valid, but it uses now to check if the token is expired. 61 func validAtTime(token, key, userID, actionID string, now time.Time) bool { 62 // Decode the token. 63 data, err := base64.URLEncoding.DecodeString(token) 64 if err != nil { 65 return false 66 } 67 68 // Extract the issue time of the token. 69 sep := bytes.LastIndex(data, []byte{':'}) 70 if sep < 0 { 71 return false 72 } 73 nanos, err := strconv.ParseInt(string(data[sep+1:]), 10, 64) 74 if err != nil { 75 return false 76 } 77 issueTime := time.Unix(0, nanos) 78 79 // Check that the token is not expired. 80 if now.Sub(issueTime) >= Timeout { 81 return false 82 } 83 84 // Check that the token is not from the future. 85 // Allow 1 minute grace period in case the token is being verified on a 86 // machine whose clock is behind the machine that issued the token. 87 if issueTime.After(now.Add(1 * time.Minute)) { 88 return false 89 } 90 91 // Check that the token matches the expected value. 92 expected := generateAtTime(key, userID, actionID, issueTime) 93 return token == expected 94 }