123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- package cookie
- // Thanks to @hoisie / [web.go](https://github.com/hoisie/web) for several of these functions
- import (
- "bytes"
- "crypto/hmac"
- "crypto/sha1"
- "encoding/base64"
- "fmt"
- "io/ioutil"
- "log"
- "net/http"
- "strconv"
- "strings"
- "time"
- )
- const (
- // Version number. Stable API within major version numbers.
- Version = 2.0
- // DefaultCookieTime represent how long login cookies should last, by deafault
- DefaultCookieTime = 24 * 3600 // 24 hours
- )
- // SecureCookie retrieves a secure cookie from a HTTP request
- func SecureCookie(req *http.Request, name string, cookieSecret string) (string, bool) {
- for _, cookie := range req.Cookies() {
- if cookie.Name != name {
- continue
- }
- parts := strings.SplitN(cookie.Value, "|", 3)
- // fix potential out of range error
- if len(parts) != 3 {
- return "", false
- }
- val := parts[0]
- timestamp := parts[1]
- signature := parts[2]
- if Signature(cookieSecret, []byte(val), timestamp) != signature {
- return "", false
- }
- ts, _ := strconv.ParseInt(timestamp, 0, 64)
- if time.Now().Unix()-31*86400 > ts {
- return "", false
- }
- buf := bytes.NewBufferString(val)
- encoder := base64.NewDecoder(base64.StdEncoding, buf)
- res, _ := ioutil.ReadAll(encoder)
- return string(res), true
- }
- return "", false
- }
- /*SetCookiePathWithFlags sets a cookie with an explicit path.
- * age is the time-to-live, in seconds (0 means forever).
- *
- * The secure and httponly flags are documented here:
- * https://golang.org/pkg/net/http/#Cookie
- */
- func SetCookiePathWithFlags(w http.ResponseWriter, name, value string, age int64, path string, secure, httponly bool) {
- var utctime time.Time
- if age == 0 {
- // 2^31 - 1 seconds (roughly 2038)
- utctime = time.Unix(2147483647, 0)
- } else {
- utctime = time.Unix(time.Now().Unix()+age, 0)
- }
- cookie := http.Cookie{Name: name, Value: value, Expires: utctime, Path: path, Secure: secure, HttpOnly: httponly}
- SetHeader(w, "Set-Cookie", cookie.String(), false)
- }
- // SetCookiePath sets a cookie with an explicit path.
- // age is the time-to-live, in seconds (0 means forever).
- func SetCookiePath(w http.ResponseWriter, name, value string, age int64, path string) {
- SetCookiePathWithFlags(w, name, value, age, path, false, false)
- }
- // ClearCookie clears the cookie with the given cookie name and a corresponding path.
- // The cookie is cleared by setting the expiration date to 1970-01-01.
- // Note that browsers *may* be configured to not delete the cookie.
- func ClearCookie(w http.ResponseWriter, cookieName, cookiePath string) {
- ignoredContent := "SNUSNU" // random string
- cookie := fmt.Sprintf("%s=%s; path=%s; expires=Thu, 01 Jan 1970 00:00:00 GMT", cookieName, ignoredContent, cookiePath)
- SetHeader(w, "Set-Cookie", cookie, true)
- }
- /*SetSecureCookiePathWithFlags creates and sets a secure cookie with an explicit path.
- * age is the time-to-live, in seconds (0 means forever).
- *
- * The secure and httponly flags are documented here:
- * https://golang.org/pkg/net/http/#Cookie
- */
- func SetSecureCookiePathWithFlags(w http.ResponseWriter, name, val string, age int64, path string, cookieSecret string, secure, httponly bool) {
- // base64 encode the value
- if len(cookieSecret) == 0 {
- log.Fatalln("Secret Key for secure cookies has not been set. Please use a non-empty secret.")
- }
- var buf bytes.Buffer
- encoder := base64.NewEncoder(base64.StdEncoding, &buf)
- encoder.Write([]byte(val))
- encoder.Close()
- vs := buf.String()
- vb := buf.Bytes()
- timestamp := strconv.FormatInt(time.Now().Unix(), 10)
- sig := Signature(cookieSecret, vb, timestamp)
- cookie := strings.Join([]string{vs, timestamp, sig}, "|")
- SetCookiePathWithFlags(w, name, cookie, age, path, secure, httponly)
- }
- // SetSecureCookiePath creates and sets a secure cookie with an explicit path.
- // age is the time-to-live, in seconds (0 means forever).
- func SetSecureCookiePath(w http.ResponseWriter, name, val string, age int64, path string, cookieSecret string) {
- SetSecureCookiePathWithFlags(w, name, val, age, path, cookieSecret, false, false)
- }
- // Signature retrieves the cookie signature
- func Signature(key string, val []byte, timestamp string) string {
- hm := hmac.New(sha1.New, []byte(key))
- hm.Write(val)
- hm.Write([]byte(timestamp))
- hex := fmt.Sprintf("%02x", hm.Sum(nil))
- return hex
- }
- // SetHeader sets cookies in the HTTP header
- func SetHeader(w http.ResponseWriter, hdr, val string, unique bool) {
- if unique {
- w.Header().Set(hdr, val)
- } else {
- w.Header().Add(hdr, val)
- }
- }
|