Safe Haskell | Safe-Inferred |
---|---|
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
Use middleware
to obtain a function to allow an Application
to
interact with a CryptoCookie
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 ((CryptoCookie a -> Application) -> Application)
- defaultConfig :: (FromJSON a, ToJSON a) => Key "AEAD_AES_256_GCM_SIV" -> Config a
- data Config (a :: Type) = forall e.Encryption e => Config {}
- autoKeyFileBase16 :: forall e m. (Encryption e, MonadIO m) => FilePath -> m (Key e)
- readKeyFileBase16 :: forall e 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 ((CryptoCookie a -> Application) -> Application) | Remember that |
Obtain a new Application
-transforming function (more or less a
Middleware
) wherein the Application
being transformed can interact
with a CryptoCookie
.
middleware
can be called multiple times as long as thesetCookieName
for theSetCookie
specified inConfig
is different each time.- It is safe to reuse the same
Key
for multiplemiddleware
calls. Each time theKey
will have a different randomlyinitial
izedEncrypt
ion context.
:: (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
data Config (a :: Type) Source #
Configuration for middleware
.
Consider using defaultConfig
and
updating desired fields only.
autoKeyFileBase16 :: forall e m. (Encryption e, MonadIO m) => FilePath -> m (Key e) Source #
readKeyFileBase16 :: forall e m. (Encryption e, MonadIO m) => FilePath -> m (Key e) Source #
Read a base-16 encoded Key
from a file. Ignores trailing newlines.