Safe Haskell | None |
---|---|
Language | GHC2021 |
This module exports tools for safely storing encrypted data on client-side cookies through Network.Wai.
This module is designed to be imported as follows:
import qualified Wai.CryptoCookie
One example of how to obtain a new Network.Wai.Middleware
,
do (middleware, lookup) <- do key <- Wai.CryptoCookie.autoKeyFileBase16
"/run/my-cookie-encryption-key" Wai.CryptoCookie.middleware
(Wai.CryptoCookie.defaultConfig
key)
The obtained middleware
shall be applied to your
Network.Wai.Application
.
The obtained lookup
function can be used to obtain the CryptoCookie
associated with each Request
. It returns Nothing
if this particular
middleware
was not used on the given Request
.
Finally, interact with the CryptoCookie
data using get
or set
.
Do I store session data on the client or on the server?
It's not so much about where to store the session data, but about how to store it and how to expire it. Here are some ideas. But please, do your own research.
- Data on server, identifier on both: In this approach, all the data is
stored on the server. On the
CryptoCookie
, simplyset
a unique session identifier and laterget
it back and use it to find the associated session data on your server-side database. In order to expire the session, all the server have to do is remove this session identifier from its database. Choose this option unless you know what you are doing, it doesn't require you to plan ahead too much. - Data on the client, identifier on both: In this approach, all the
data and session identifier is stored on the
CryptoCookie
. On your server-side database, store the session identifier and a timestamp representing its creation time or last session activity time. Before accepting the session data from theCryptoCookie
as valid, check that the session identifier exists in your database, and that the time since the timestamp is acceptable. This approach is simpler on your server-side database, but it can lead to more network traffic, and schema migrations for session data will be complex if you care about backwards compatibility with currently active sessions. - Everything on the client: You can store everything in the
CryptoCookie
. However, you will be more suceptible to replay attacks because you won't have control over session expiration beyond comparing the current time against the session creation timestamp or last activity timestamp previously set in the session data. You can force all the existing sessions to “expire” by rotating your encryptionKey
. Also, this approach can lead to more network traffic, and schema migrations for session data will be complex if you care about backwards compatibility with currently active sessions.
Synopsis
- data CryptoCookie a
- get :: CryptoCookie a -> Maybe a
- set :: CryptoCookie a -> Maybe a -> STM ()
- middleware :: forall a m. MonadIO m => Config a -> m (Middleware, Request -> Maybe (CryptoCookie a))
- defaultConfig :: (FromJSON a, ToJSON a) => Key "AEAD_AES_256_GCM_SIV" -> Config a
- data Config a = Encryption e => Config {}
- autoKeyFileBase16 :: forall {k} (e :: k) m. (Encryption e, MonadIO m) => FilePath -> m (Key e)
- readKeyFileBase16 :: forall {k} (e :: k) m. (Encryption e, MonadIO m) => FilePath -> m (Key e)
Cookie data
data CryptoCookie a Source #
Read-write access to the Wai.CryptoCookie data.
Middleware
:: forall a m. MonadIO m | |
=> Config a | Consider using |
-> m (Middleware, Request -> Maybe (CryptoCookie a)) |
Construct a new Middleware
, and function that can be used to look-up the
CryptoCookie
on each incoming Request
. Said function returns
Nothing
if the Middleware
was not used on the Request
.
:: (FromJSON a, ToJSON a) | |
=> Key "AEAD_AES_256_GCM_SIV" | Consider using |
-> Config a |
Default Config
:
Encoding
isaeson
.Encryption
scheme is the nonce-misuse resistantAEAD_AES_256_GCM_SIV
as defined in in RFC 8452.As an AEAD encryption scheme, you can be confident that a successfully decrypted cookie could only have been encrypted by the same
Key
. This makes this encryption scheme suitable for storing user session authentication identifiers generated by the server.Cookie name is
SESSION
.HttpOnly
: yesMax-Age
: 16 hoursPath
:/
SameSite
:Lax
Secure
: yesDomain
: not set
Configuration for middleware
.
Consider using defaultConfig
and
updating desired fields only.
autoKeyFileBase16 :: forall {k} (e :: k) m. (Encryption e, MonadIO m) => FilePath -> m (Key e) Source #
readKeyFileBase16 :: forall {k} (e :: k) m. (Encryption e, MonadIO m) => FilePath -> m (Key e) Source #
Read a base-16 encoded Key
from a file. Ignores trailing newlines.