Package gorilla/securecookie encodes and decodes authenticated and optionally encrypted cookie values for Go web applications.

securecookie

GoDoc Build Status Sourcegraph

securecookie encodes and decodes authenticated and optionally encrypted cookie values.

Secure cookies can't be forged, because their values are validated using HMAC. When encrypted, the content is also inaccessible to malicious eyes. It is still recommended that sensitive data not be stored in cookies, and that HTTPS be used to prevent cookie replay attacks.

Examples

To use it, first create a new SecureCookie instance:

// Hash keys should be at least 32 bytes long
var hashKey = []byte("very-secret")
// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
// Shorter keys may weaken the encryption used.
var blockKey = []byte("a-lot-secret")
var s = securecookie.New(hashKey, blockKey)

The hashKey is required, used to authenticate the cookie value using HMAC. It is recommended to use a key with 32 or 64 bytes.

The blockKey is optional, used to encrypt the cookie value -- set it to nil to not use encryption. If set, the length must correspond to the block size of the encryption algorithm. For AES, used by default, valid lengths are 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.

Strong keys can be created using the convenience function GenerateRandomKey(). Note that keys created using GenerateRandomKey() are not automatically persisted. New keys will be created when the application is restarted, and previously issued cookies will not be able to be decoded.

Once a SecureCookie instance is set, use it to encode a cookie value:

func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
	value := map[string]string{
		"foo": "bar",
	}
	if encoded, err := s.Encode("cookie-name", value); err == nil {
		cookie := &http.Cookie{
			Name:  "cookie-name",
			Value: encoded,
			Path:  "/",
			Secure: true,
			HttpOnly: true,
		}
		http.SetCookie(w, cookie)
	}
}

Later, use the same SecureCookie instance to decode and validate a cookie value:

func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
	if cookie, err := r.Cookie("cookie-name"); err == nil {
		value := make(map[string]string)
		if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
			fmt.Fprintf(w, "The value of foo is %q", value["foo"])
		}
	}
}

We stored a map[string]string, but secure cookies can hold any value that can be encoded using encoding/gob. To store custom types, they must be registered first using gob.Register(). For basic types this is not needed; it works out of the box. An optional JSON encoder that uses encoding/json is available for types compatible with JSON.

Key Rotation

Rotating keys is an important part of any security strategy. The EncodeMulti and DecodeMulti functions allow for multiple keys to be rotated in and out. For example, let's take a system that stores keys in a map:

// keys stored in a map will not be persisted between restarts
// a more persistent storage should be considered for production applications.
var cookies = map[string]*securecookie.SecureCookie{
	"previous": securecookie.New(
		securecookie.GenerateRandomKey(64),
		securecookie.GenerateRandomKey(32),
	),
	"current": securecookie.New(
		securecookie.GenerateRandomKey(64),
		securecookie.GenerateRandomKey(32),
	),
}

Using the current key to encode new cookies:

func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
	value := map[string]string{
		"foo": "bar",
	}
	if encoded, err := securecookie.EncodeMulti("cookie-name", value, cookies["current"]); err == nil {
		cookie := &http.Cookie{
			Name:  "cookie-name",
			Value: encoded,
			Path:  "/",
		}
		http.SetCookie(w, cookie)
	}
}

Later, decode cookies. Check against all valid keys:

func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
	if cookie, err := r.Cookie("cookie-name"); err == nil {
		value := make(map[string]string)
		err = securecookie.DecodeMulti("cookie-name", cookie.Value, &value, cookies["current"], cookies["previous"])
		if err == nil {
			fmt.Fprintf(w, "The value of foo is %q", value["foo"])
		}
	}
}

Rotate the keys. This strategy allows previously issued cookies to be valid until the next rotation:

func Rotate(newCookie *securecookie.SecureCookie) {
	cookies["previous"] = cookies["current"]
	cookies["current"] = newCookie
}

License

BSD licensed. See the LICENSE file for details.

Owner
Gorilla Web Toolkit
Gorilla is a web toolkit for the Go programming language that provides useful, composable packages for writing HTTP-based applications.
Gorilla Web Toolkit
Comments
  • Improve Decode against timing attacks

    Improve Decode against timing attacks

    The current implementation of Decode allows very easily with timing attacks to find out which error occurred. I improved it, but there are probably still some room for improvement.

  • Make GenerateRandomKey panic on error

    Make GenerateRandomKey panic on error

    Previously GenerateRandomKey returned nil if it failed to read random bytes from crypto/rand Reader. A quick GitHub code search:

    https://github.com/search?utf8=%E2%9C%93&q=securecookie.GenerateRandomKey&type=Code&ref=searchresults

    reveals that most people don't bother to check the return value, thus, if their random generator fails for some reason, they'll get empty keys.

    We fix this by making GenerateRandomKey panic on error, which is a common and safe way to deal with such cases if there's no error return.

  • Save Encoder/Decoder: 9x fewer allocs; 6x faster.

    Save Encoder/Decoder: 9x fewer allocs; 6x faster.

    This PR is meant to address performance and allocation overhead. Per the gob/encoding documentation:

    "...[it] is most efficient when a single Encoder is used to transmit a stream of values, amortizing the cost of compilation."

    The existing implementation created and destroyed gob.Encoders and gob.Decoders on every call to serialize and deserialize, respectively.

    I changed the SecureCookie object to maintain a buffer, encoder, and decoder to re-use. There is a significant difference in speed and memory allocation overhead:

    | Implementation | time/op | mem/op | allocs/op | | --- | :-: | :-: | :-: | | Old | 75,275 ns | 21,396 B | 369 | | New | 17,843 ns | 3,475 B | 39 |

    (I've added the benchmark in the testing file for you to see for yourself.)

    The downside here is that the SecureCookie object has to lock around serialization calls. This could potentially be improved by maintaining an array of buffers and doing fine-grained locking. (But then we would have a really fat struct.)

    Looking forward to your comments, Phil

    Review on Reviewable

  • Maybe the performance of the function GenerateRandomKey can be greatly improved

    Maybe the performance of the function GenerateRandomKey can be greatly improved

    Maybe using "crypto/rand" to generate the key is not efficient as mentioned in: http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go

    I think maybe you can adopt some points from https://github.com/dustin/randbo/blob/master/randbo.go .

    Thank you very much.

  • securecookie: base64 decode failed - caused by: illegal base64 data at input byte 216

    securecookie: base64 decode failed - caused by: illegal base64 data at input byte 216

    Code

    Session store

    secret := securecookie.GenerateRandomKey(64)
    if secret == nil {
    	glg.Fatalf("The system random number generator failed")
    }
    srv.sessionStore = sessions.NewCookieStore(secret)
    

    Session get

    func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
    	sess, err := s.sessionStore.Get(r, sessionCookieName)
    	glg.Infof("Session is new: %v", sess.IsNew)
    	if err != nil {
    		glg.Errorf("read session: %v", err)
    		sess, err = s.sessionStore.New(r, sessionCookieName)
    		if err != nil {
    			glg.Errorf("new session: %v", err)
    			return
    		}
    	}
    }
    

    Output

    2018-08-30 16:59:24     [INFO]: Session is new: true
    2018-08-30 16:52:10     [ERR]:  read session: securecookie: base64 decode failed - caused by: illegal base64 data at input byte 216
    2018-08-30 16:52:10     [ERR]:  new session: securecookie: base64 decode failed - caused by: illegal base64 data at input byte 216
    

    At the point where this error occurs, the session is new and no data has been written into it's Values whatsoever. I can't explain this error, and hoped maybe one of you can help me here? I'd be happy to provide further information.

  • add more compact encoding

    add more compact encoding

    Current encoding does Base64 twice against payload because of intermediate text encoding. And it produces too large mac (256bit) while 128bit is more than enough.

    Add "compact" mode which uses binary encoding for message with single Base64 pass and 128bit hmac output (at max).

        TestSecureCookie: securecookie_test.go:48: i 0 len 184                                                         
        TestSecureCookie: securecookie_test.go:48: i 1 len 128                                                         
        TestSecureCookie: securecookie_test.go:48: i 2 len 188                                                         
        TestSecureCookie: securecookie_test.go:48: i 3 len 128                                                         
        TestSecureCookie: securecookie_test.go:48: i 4 len 188                                                         
        TestSecureCookie: securecookie_test.go:48: i 5 len 128                                                         
        TestSecureCookie: securecookie_test.go:48: i 6 len 188                                                         
        TestSecureCookie: securecookie_test.go:48: i 7 len 128                                                         
        TestSecureCookie: securecookie_test.go:48: i 8 len 188                                                         
        TestSecureCookie: securecookie_test.go:48: i 9 len 128                                                         
    
  • Make errors more distinguishable

    Make errors more distinguishable

    Prior to this commit, this library raised errors either mostly using errors.New() or directly passing through error values from underlying libraries. This made it difficult for clients to respond correctly to the errors that were returned.

    This becomes particularly problematic when securecookie is used together with gorilla/sessions. From an operations standpoint, you often want to log different errors when the client simply provides an invalid auth cookie, versus an I/O error fetching data from the session store. The former probably indicates an expired timestamp or similar client error; the latter indicates a possible failure in a backend database.

    This commit introduces a public Error interface, which is now returned consistently on all errors, and can be used to distinguish between implementation errors (IsUsage() and IsInternal()) and failed validation of user input (IsDecode()).

    See also discussion on pull requests #9 and #24: https://github.com/gorilla/securecookie/pull/9 https://github.com/gorilla/securecookie/pull/24

    Some interface comments on other API functions have been clarified and updated to harmonize with the new error interfaces.

  • Cookie not returned from the browser

    Cookie not returned from the browser

    Hello,

    I am having issues using securecookie. When I encode a value and send it to the browser, it is sometimes not returning it back. Seems to me like the browser doesn't like some of the characters in the encoded cookie value. I am using Google Chrome, up to date.

    Tried replacing base64 with base32.StdEncoding in this package and also replacing the padding = with 0, which is ok for base32.StdEncoding, and it all started working.

    So, I am basically asking where the issues is. How come this package is working for other people?

    In case I don't want to fork this package, what am I supposed to do? Encode the value returned from securecookie with base32? That will only increase the cookie size, although it is not really dramatic for my use case...

    Thanks!

  • Exposing the cookie timestamp

    Exposing the cookie timestamp

    I have a use case where I want to refresh the cookie and its data after a certain time period. This period is much shorter than the maximum age of the cookie and the cookie's validity shouldn't be affected by this shorter refresh cycle.

    I could include a timestamp inside my cookie data, but that seems highly redundant as there is already a HMAC verified timestamp in the cookie and I would rather save some bandwidth. Unfortunately this timestamp isn't exposed in any way currently, so this is where my patch comes in.

    An additional return value from the Decode method would be the safest & most straightforward solution. It would break API compatibility however, so probably not that likely to get merged.

    I gathered from issue #14 that thread safety isn't a goal of this library. This opened up another fairly easy solution, which I have implemented. Successful enough calls to Decode will update the new LastTimestamp property of the main SecureCookie struct instance with the cookie's timestamp.

  • Added support for encoding/json

    Added support for encoding/json

    Added a new Encoder interface with serialize and deserialize methods. The existing encoding/gob remains the default for compatibility reasons. Package users who wish to use the (often faster) encoding/json package can do so at the cost of some extra LOC for custom types.

    At the moment the interface and its methods are private as I wanted to avoid expanding the public API too much. If there's a desire to have it public so that others can satisfy Encoder in their own code then I'm happy to change it.

    I also added a new JSON test based on the existing gob test for completeness.

  • How to handle error

    How to handle error "securecookie: expired timestamp"

    I get error securecookie: expired timestamp even though I don't set an expiration date on the cookie. (I suspect there's a date in the encrypted cookie.) Here is my logic:

    	co, err := sc.Encode("user", user.ID)
    	if err != nil {
    		return errors.Wrap(err, "failed to encode the token")
    	}
    
    	http.SetCookie(w, &http.Cookie{
    		Name:  "user",
    		Path:  "/",
    		Value: co,
    	})
    

    What am I supposed to do with this error?

    The errTimestampExpired is not exported from the package, so that doesn't give me a good way to check whether an error is indeed expiration error. What should a server do when they get this error? Clear cookies to unblock the user?

  • docs: changed deprecated goo.gl links to their unshortened variant

    docs: changed deprecated goo.gl links to their unshortened variant

    Unshortened the links in the docs because

    1. goo.gl is a deprecated service
    2. being able to see the actual link is more verbose thus more useful inside of docs
  • “securecookie/fuzz/corpus/c63ae6dd4fc9f9dda66970e827d13f7c73fe841c-1”Why is this file reported by antivirus software?

    “securecookie/fuzz/corpus/c63ae6dd4fc9f9dda66970e827d13f7c73fe841c-1”Why is this file reported by antivirus software?

    Describe the problem you're having

    A clear and concise description of what the bug is.

    Versions

    Go version: go version

    package version: run git rev-parse HEAD inside the repo

    "Show me the code!"

    A minimal code snippet can be useful, otherwise we're left guessing!

    Hint: wrap it with backticks to format it

  • corresponding Java implementation

    corresponding Java implementation

    Excuse me, Does this algorithm have a corresponding Java implementation?

    There is a Go Projection encode cookie by using this algorithm, and now , I want decode the cookie in Java Projection.

    I can not find a java lib about decoding cookie by using this algorithm.

    Please Help Me~~~

  • Add compact encoding (v2)

    Add compact encoding (v2)

    Current encoding does Base64 twice against payload because of intermediate text encoding. Also it adds 16byte nonce to value (and it is also expanded twice by Base64) and 32byte MAC with default settings. Total overhead of added data is (12+32+(16*4/3))*4/3 == 87 bytes, and value is expanded in (4/3)*(4/3)=1.77 times.

    Summary of Changes

    This PR adds compact encoding for securecookie:

    • value is passed through Base64 only once together with MAC and time
    • there is no separate nonce. MAC is calculated on plaintext + time (with submillisecond granularity) and then used as nonce for stream cipher.
    • migration is easy because it may decode both versions guessing by first base64 byte
    • algorithms are fixed to Blake2s and Chacha20 cause they are proven to be secure and fast on all common architectures (32/64 bit, appengine).

    Total overhead data is (1+15+8)*4/3 = 32 bytes. Value expanded only once in 1.33 times.

    Note: MAC is 15byte (120bit) which is certainly enough for this use case.

    It saves a lot of cpu time and allocations:

    BenchmarkLegacyEncode-8     239144   4504 ns/op  3445 B/op   21 allocs/op
    BenchmarkLegacyEncode-8     261038   4499 ns/op  3442 B/op   21 allocs/op
    BenchmarkCompactEncode-8    933446   1304 ns/op   721 B/op    4 allocs/op
    BenchmarkCompactEncode-8    927328   1331 ns/op   721 B/op    4 allocs/op
    BenchmarkLegacyDecode-8     298273   3923 ns/op  2312 B/op   17 allocs/op
    BenchmarkLegacyDecode-8     298401   3944 ns/op  2367 B/op   17 allocs/op
    BenchmarkCompactDecode-8    806112   1359 ns/op   465 B/op    3 allocs/op
    BenchmarkCompactDecode-8    905617   1348 ns/op   471 B/op    3 allocs/op
    
  • Update README.md

    Update README.md

    Typo in Example Code?

    Fixes # Appears to be typo in example code.

    Summary of Changes

    1. change s2 to s

    PS: Make sure your PR includes/updates tests! If you need help with this part, just ask!

  • difference to JWT

    difference to JWT

    Not really a bug or a feature request - but a request for clarification in the README.

    Why would one use the this project over JWT? JWT also supports signing and encryption - and is a standard.

    I am not making a case for either - but it would be nice to read a position on this.

Cookie - Cookie helper functions for golang

Cookie Cookie helper functions. package main import ( "net/http" "github.co

Feb 10, 2022
Authenticated and encrypted API tokens using modern crypto

Branca Token Authenticated and encrypted API tokens using modern crypto. What? Branca is a secure, easy to use token format which makes it hard to sho

Dec 25, 2022
:key: Secure alternative to JWT. Authenticated Encrypted API Tokens for Go.

branca branca is a secure alternative to JWT, This implementation is written in pure Go (no cgo dependencies) and implements the branca token specific

Dec 29, 2022
Authenticated encrypted API tokens (IETF XChaCha20-Poly1305 AEAD) for Golang

branca.go is branca token specification implementation for Golang 1.15+.

Dec 26, 2022
golang csrf react example, using gorilla/mux and gorilla/mux

Demo REST backend Gorilla csrf middleware and Js frontend Use gorilla/mux and gorilla/csrf How to run open goland IDE, run middleware_test.go by click

Feb 2, 2022
Fast, secure and efficient secure cookie encoder/decoder

Encode and Decode secure cookies This package provides functions to encode and decode secure cookie values. A secure cookie has its value ciphered and

Dec 9, 2022
Advent of Code Input Loader, provide a session cookie and a problem date, returns a string or []byte of the input

Advent of Code Get (aocget) A small lib to download your puzzle input for a given day. Uses your session token to authenticate to obtain your personal

Dec 9, 2021
Goauth - Basic username password cookie based authentication with Go Lang

goauth [WIP] Basic username password cookie based authentication with Go Lang Overview Use a Postgres DB to store Sign-in and Sign-up info Redis for c

Jan 4, 2022
Cocos2d-x texture unpacker, primarily for Cookie Run.

boofunpack Cocos2d-x texture unpacker, primarily for Cookie Run: OvenBreak and Cookie Run for Kakao/LINE (though it likely works for other .plist form

Oct 11, 2022
Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applications.

Goth: Multi-Provider Authentication for Go Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applic

Dec 29, 2022
Aegis To KeePass - A simple tool to convert exported (and encrypted) Aegis database to standalone KeePass

ATP - Aegis to KeePass A simple tool to convert exported (and encrypted) Aegis d

Aug 28, 2022
Certificate authority and access plane for SSH, Kubernetes, web applications, and databases

Teleport is an identity-aware, multi-protocol access proxy which understands SSH, HTTPS, Kubernetes API, MySQL and PostgreSQL wire protocols.

Jan 9, 2023
Minimalistic RBAC package for Go applications

RBAC Overview RBAC is a package that makes it easy to implement Role Based Access Control (RBAC) models in Go applications. Download To download this

Oct 25, 2022
jwt package for gin go applications

gin-jwt jwt package for gin go applications Usage Download using go module: go get github.com/ennaque/gin-jwt Import it in your code: import gwt "gith

Apr 21, 2022
This package provides json web token (jwt) middleware for goLang http servers

jwt-auth jwt auth middleware in goLang. If you're interested in using sessions, checkout my sessions library! README Contents: Quickstart Performance

Dec 5, 2022
The mep-agent module provides proxy services for 3rd applications to MEP.

Mep-Agent Introduction Mep-Agent is a middleware that provides proxy services for third-party apps. It can help apps, which do not implement the ETSI

Mar 9, 2022
A library for Go client applications that need to perform OAuth authorization against a server
A library for Go client applications that need to perform OAuth authorization against a server

oauth-0.8.0.zip oauth A library for Go client applications that need to perform OAuth authorization against a server, typically GitHub.com. Traditiona

Oct 13, 2021
Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to create powerful modern API and web authentication.

❗ Cache package has been moved to libcache repository Go-Guardian Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to

Dec 23, 2022
A simple and lightweight library for creating, formatting, manipulating, signing, and validating JSON Web Tokens in Go.

GoJWT - JSON Web Tokens in Go GoJWT is a simple and lightweight library for creating, formatting, manipulating, signing and validating Json Web Tokens

Nov 15, 2022