cookie.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package cookie
  2. // Thanks to @hoisie / [web.go](https://github.com/hoisie/web) for several of these functions
  3. import (
  4. "bytes"
  5. "crypto/hmac"
  6. "crypto/sha1"
  7. "encoding/base64"
  8. "fmt"
  9. "io/ioutil"
  10. "log"
  11. "net/http"
  12. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. const (
  17. // Version number. Stable API within major version numbers.
  18. Version = 2.0
  19. // DefaultCookieTime represent how long login cookies should last, by deafault
  20. DefaultCookieTime = 24 * 3600 // 24 hours
  21. )
  22. // SecureCookie retrieves a secure cookie from a HTTP request
  23. func SecureCookie(req *http.Request, name string, cookieSecret string) (string, bool) {
  24. for _, cookie := range req.Cookies() {
  25. if cookie.Name != name {
  26. continue
  27. }
  28. parts := strings.SplitN(cookie.Value, "|", 3)
  29. // fix potential out of range error
  30. if len(parts) != 3 {
  31. return "", false
  32. }
  33. val := parts[0]
  34. timestamp := parts[1]
  35. signature := parts[2]
  36. if Signature(cookieSecret, []byte(val), timestamp) != signature {
  37. return "", false
  38. }
  39. ts, _ := strconv.ParseInt(timestamp, 0, 64)
  40. if time.Now().Unix()-31*86400 > ts {
  41. return "", false
  42. }
  43. buf := bytes.NewBufferString(val)
  44. encoder := base64.NewDecoder(base64.StdEncoding, buf)
  45. res, _ := ioutil.ReadAll(encoder)
  46. return string(res), true
  47. }
  48. return "", false
  49. }
  50. /*SetCookiePathWithFlags sets a cookie with an explicit path.
  51. * age is the time-to-live, in seconds (0 means forever).
  52. *
  53. * The secure and httponly flags are documented here:
  54. * https://golang.org/pkg/net/http/#Cookie
  55. */
  56. func SetCookiePathWithFlags(w http.ResponseWriter, name, value string, age int64, path string, secure, httponly bool) {
  57. var utctime time.Time
  58. if age == 0 {
  59. // 2^31 - 1 seconds (roughly 2038)
  60. utctime = time.Unix(2147483647, 0)
  61. } else {
  62. utctime = time.Unix(time.Now().Unix()+age, 0)
  63. }
  64. cookie := http.Cookie{Name: name, Value: value, Expires: utctime, Path: path, Secure: secure, HttpOnly: httponly}
  65. SetHeader(w, "Set-Cookie", cookie.String(), false)
  66. }
  67. // SetCookiePath sets a cookie with an explicit path.
  68. // age is the time-to-live, in seconds (0 means forever).
  69. func SetCookiePath(w http.ResponseWriter, name, value string, age int64, path string) {
  70. SetCookiePathWithFlags(w, name, value, age, path, false, false)
  71. }
  72. // ClearCookie clears the cookie with the given cookie name and a corresponding path.
  73. // The cookie is cleared by setting the expiration date to 1970-01-01.
  74. // Note that browsers *may* be configured to not delete the cookie.
  75. func ClearCookie(w http.ResponseWriter, cookieName, cookiePath string) {
  76. ignoredContent := "SNUSNU" // random string
  77. cookie := fmt.Sprintf("%s=%s; path=%s; expires=Thu, 01 Jan 1970 00:00:00 GMT", cookieName, ignoredContent, cookiePath)
  78. SetHeader(w, "Set-Cookie", cookie, true)
  79. }
  80. /*SetSecureCookiePathWithFlags creates and sets a secure cookie with an explicit path.
  81. * age is the time-to-live, in seconds (0 means forever).
  82. *
  83. * The secure and httponly flags are documented here:
  84. * https://golang.org/pkg/net/http/#Cookie
  85. */
  86. func SetSecureCookiePathWithFlags(w http.ResponseWriter, name, val string, age int64, path string, cookieSecret string, secure, httponly bool) {
  87. // base64 encode the value
  88. if len(cookieSecret) == 0 {
  89. log.Fatalln("Secret Key for secure cookies has not been set. Please use a non-empty secret.")
  90. }
  91. var buf bytes.Buffer
  92. encoder := base64.NewEncoder(base64.StdEncoding, &buf)
  93. encoder.Write([]byte(val))
  94. encoder.Close()
  95. vs := buf.String()
  96. vb := buf.Bytes()
  97. timestamp := strconv.FormatInt(time.Now().Unix(), 10)
  98. sig := Signature(cookieSecret, vb, timestamp)
  99. cookie := strings.Join([]string{vs, timestamp, sig}, "|")
  100. SetCookiePathWithFlags(w, name, cookie, age, path, secure, httponly)
  101. }
  102. // SetSecureCookiePath creates and sets a secure cookie with an explicit path.
  103. // age is the time-to-live, in seconds (0 means forever).
  104. func SetSecureCookiePath(w http.ResponseWriter, name, val string, age int64, path string, cookieSecret string) {
  105. SetSecureCookiePathWithFlags(w, name, val, age, path, cookieSecret, false, false)
  106. }
  107. // Signature retrieves the cookie signature
  108. func Signature(key string, val []byte, timestamp string) string {
  109. hm := hmac.New(sha1.New, []byte(key))
  110. hm.Write(val)
  111. hm.Write([]byte(timestamp))
  112. hex := fmt.Sprintf("%02x", hm.Sum(nil))
  113. return hex
  114. }
  115. // SetHeader sets cookies in the HTTP header
  116. func SetHeader(w http.ResponseWriter, hdr, val string, unique bool) {
  117. if unique {
  118. w.Header().Set(hdr, val)
  119. } else {
  120. w.Header().Add(hdr, val)
  121. }
  122. }