Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

xsrf.go 3.2KB

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