package
0.1.1
Repository: https://github.com/golangcollege/scs.git
Documentation: pkg.go.dev

# README

cookiestore

godoc

Package cookiestore is a cookie-based storage engine for the SCS session package.

It stores session data in AES-encrypted and SHA256-signed cookies on the client. Key rotation is supported for increased security.

The cookiestore package provides a simple and easy way to implement session functionality, with no external dependencies.

Usage

Installation

Either:

$ go get github.com/alexedwards/scs/engine/cookiestore

Or (recommended) use use gvt to vendor the engine/cookiestore and session sub-packages:

$ gvt fetch github.com/alexedwards/scs/engine/cookiestore
$ gvt fetch github.com/alexedwards/scs/session

Example

package main

import (
    "io"
    "log"
    "net/http"

    "github.com/alexedwards/scs/engine/cookiestore"
    "github.com/alexedwards/scs/session"
)

// HMAC authentication key (hexadecimal representation of 32 random bytes)
var hmacKey = []byte("f71dc7e58abab014ddad2652475056f185164d262869c8931b239de52711ba87")

// AES encryption key (hexadecimal representation of 16 random bytes)
var blockKey = []byte("911182cec2f206986c8c82440adb7d17")

func main() {
    // Create a new keyset using your authentication and encryption secret keys.
    keyset, err := cookiestore.NewKeyset(hmacKey, blockKey)
    if err != nil {
        log.Fatal(err)
    }

    // Create a new CookieStore instance using the keyset.
    engine := cookiestore.New(keyset)

    sessionManager := session.Manage(engine)
    http.HandleFunc("/put", putHandler)
    http.HandleFunc("/get", getHandler)
    http.ListenAndServe(":4000", sessionManager(http.DefaultServeMux))
}

func putHandler(w http.ResponseWriter, r *http.Request) {
    err := session.PutString(r, "message", "Hello world!")
    if err != nil {
        http.Error(w, err.Error(), 500)
    }
}

func getHandler(w http.ResponseWriter, r *http.Request) {
    msg, err := session.GetString(r, "message")
    if err != nil {
        http.Error(w, err.Error(), 500)
    }
    io.WriteString(w, msg)
}

Creating a Keyset

Every CookieStore instance must have a Keyset, which contains your secret keys used for encrypting/decrypting the session data and signing the session cookie.

Keysets are created with the NewKeyset() function, which takes an hmacKey parameter (used to create the HMAC hash to sign the session cookie) and a blockKey parameter (used to encrypt/decrypt the session data).

var hmacKey = []byte("f71dc7e58abab014ddad2652475056f185164d262869c8931b239de52711ba87")
var blockKey = []byte("911182cec2f206986c8c82440adb7d17")

keyset, err := cookiestore.NewKeyset(hmacKey, blockKey)
if err != nil {
    log.Fatal(err)
}

Because cookiestore uses SHA256 as the HMAC hashing algorithm, the recommended minimum length of the hmacKey parameter is at least 32 random bytes. If you're storing the key as an encoded string for convenience, the underlying entropy should still be 32 bytes (i.e you should use a 64 character hex string or 43 character base64 string).

The blockKey must be 16, 24 or 32 bytes long. The byte length you use will control which AES implementation is used. A 16 byte blockKey will mean that AES-128 is used to encrypt the session data, 24 bytes means AES-192 will be used, and 32 bytes means that AES-256 will be used.

Unencrypted session cookies

Session cookies that are signed, but not encrypted, can also be used. The cookies will remain tamper-proof, but an user or attacker will be able to read the session data in the cookie.

Using unencrypted session cookies is marginally faster.

Creating a Keyset with the NewUnencryptedKeyset() function will result in unencrypted cookies being used.

var hmacKey = []byte("f71dc7e58abab014ddad2652475056f185164d262869c8931b239de52711ba87")

keyset, err := cookiestore.NewUnencryptedKeyset(hmacKey)
if err != nil {
    log.Fatal(err)
}

engine := cookiestore.New(keyset)

Key rotation

The cookiestore package supports key rotation for increased security.

An arbitrary number of old Keysets can be provided when creating a new CookieStore instance. For example:

keyset, err := cookiestore.NewKeyset([]byte("f71dc7e58abab014ddad2652475056f185164d262869c8931b239de52711ba87"), []byte("911182cec2f206986c8c82440adb7d17"))
if err != nil {
    log.Fatal(err)
}

oldKeyset, err := cookiestore.NewKeyset([]byte("16bd76c6372363cd9af46f5619cc406776210b6164c48fd1200119d4cfc359e6"), []byte("5f8b7a8efac2a900a0c6be609b2e0241"))
if err != nil {
    log.Fatal(err)
}

veryOldKeyset, err := cookiestore.NewKeyset([]byte("0c03fa487baa82dda09c4f12c7238370c58112a135318a6e3d4a4724a95cd2e0"), []byte("46ee77bfb95a765dfefca83bf53d5914"))
if err != nil {
    log.Fatal(err)
}

engine := cookiestore.New(keyset, oldKeyset, veryOldKeyset)

When a session cookie is received from a client, all Keysets (including old Keysets) are looped through to try to decode the cookie.

When rotating Keysets, it is essential that Keysets are entirely unique. You must not change the the blockKey for a Keyset without also changing the hmacKey. Re-using hmacKey values will result in some valid cookies not being able to be decoded.

Cookie length

Cookies are limited to 4096 characters in length. Storing large amounts of session data may, when encoded and signed, exceed this length and result in an error.

This makes cookie-based sessions suitable for applications where the amount of session data is known in advance and small.

RegenerateToken function

The cookiestore package is a special case for the scs/session package because it stores data on the client only, not the server.

This means that using session.RegenerateToken() as a mechanism to prevent session fixation attacks is unnecessary when using cookiestore, because the signed and encrypted cookie 'token' always changes whenever the session data is modified anyway.

The only impact that calling session.RegenerateToken() will have is to reset and restart the session lifetime.

Notes

Full godoc documentation: https://godoc.org/github.com/alexedwards/scs/engine/cookiestore.

# Functions

New returns a new CookieStore instance.
NewKeyset returns a pointer to a Keyset, which contains your secret keys used for encrypting/decrypting the session data and signing the session cookie.
NewUnencryptedKeyset returns a pointer to a Keyset which will sign, but not encrypt, the session cookie.

# Structs

CookieStore represents the currently configured session storage engine.
Keyset holds the secrets for signing and encrypting/decrypting session cookies.