Packages
plug
1.0.6
1.20.3
1.20.2
1.20.1
retired
1.20.0
retired
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.15.6
1.15.5
1.15.4
1.15.3
1.15.2
1.15.1
1.15.0
1.14.2
1.14.1
1.14.0
1.13.6
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
retired
1.13.0
retired
1.12.1
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.0
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.14.0
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
Compose web applications with functions
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/plug/session/cookie.ex
defmodule Plug.Session.COOKIE do
@moduledoc """
Stores the session in a cookie.
This cookie store is based on `Plug.Crypto.MessageVerifier`
and `Plug.Crypto.Message.Encryptor` which encrypts and signs
each cookie to ensure they can't be read nor tampered with.
Since this store uses crypto features, it requires you to
set the `:secret_key_base` field in your connection. This
can be easily achieved with a plug:
plug :put_secret_key_base
def put_secret_key_base(conn, _) do
put_in conn.secret_key_base, "-- LONG STRING WITH AT LEAST 64 BYTES --"
end
## Options
* `:encryption_salt` - a salt used with `conn.secret_key_base` to generate
a key for encrypting/decrypting a cookie.
* `:signing_salt` - a salt used with `conn.secret_key_base` to generate a
key for signing/verifying a cookie;
* `:key_iterations` - option passed to `Plug.Crypto.KeyGenerator`
when generating the encryption and signing keys. Defaults to 1000;
* `:key_length` - option passed to `Plug.Crypto.KeyGenerator`
when generating the encryption and signing keys. Defaults to 32;
* `:key_digest` - option passed to `Plug.Crypto.KeyGenerator`
when generating the encryption and signing keys. Defaults to `:sha256';
* `:serializer` - cookie serializer module that defines `encode/1` and
`decode/1` returning an `{:ok, value}` tuple. Defaults to
`:external_term_format`.
## Examples
# Use the session plug with the table name
plug Plug.Session, store: :cookie,
key: "_my_app_session",
encryption_salt: "cookie store encryption salt",
signing_salt: "cookie store signing salt",
key_length: 64
"""
@behaviour Plug.Session.Store
alias Plug.Crypto.KeyGenerator
alias Plug.Crypto.MessageVerifier
alias Plug.Crypto.MessageEncryptor
def init(opts) do
encryption_salt = opts[:encryption_salt]
signing_salt = check_signing_salt(opts)
iterations = Keyword.get(opts, :key_iterations, 1000)
length = Keyword.get(opts, :key_length, 32)
digest = Keyword.get(opts, :key_digest, :sha256)
key_opts = [iterations: iterations,
length: length,
digest: digest,
cache: Plug.Keys]
serializer = check_serializer(opts[:serializer] || :external_term_format)
%{encryption_salt: encryption_salt,
signing_salt: signing_salt,
key_opts: key_opts,
serializer: serializer}
end
def get(conn, cookie, opts) do
key_opts = opts.key_opts
if key = opts.encryption_salt do
MessageEncryptor.verify_and_decrypt(cookie,
derive(conn, key, key_opts),
derive(conn, opts.signing_salt, key_opts))
else
MessageVerifier.verify(cookie, derive(conn, opts.signing_salt, key_opts))
end |> decode(opts.serializer)
end
def put(conn, _sid, term, opts) do
binary = encode(term, opts.serializer)
key_opts = opts.key_opts
if key = opts.encryption_salt do
MessageEncryptor.encrypt_and_sign(binary,
derive(conn, key, key_opts),
derive(conn, opts.signing_salt, key_opts))
else
MessageVerifier.sign(binary, derive(conn, opts.signing_salt, key_opts))
end
end
def delete(_conn, _sid, _opts) do
:ok
end
defp encode(term, :external_term_format) do
:erlang.term_to_binary(term)
end
defp encode(term, serializer) do
{:ok, binary} = serializer.encode(term)
binary
end
defp decode({:ok, binary}, :external_term_format) do
{:term,
try do
Plug.Crypto.safe_binary_to_term(binary)
rescue
_ -> %{}
end}
end
defp decode({:ok, binary}, serializer) do
case serializer.decode(binary) do
{:ok, term} -> {:custom, term}
_ -> {:custom, %{}}
end
end
defp decode(:error, _serializer) do
{nil, %{}}
end
defp derive(conn, key, key_opts) do
conn.secret_key_base
|> validate_secret_key_base()
|> KeyGenerator.generate(key, key_opts)
end
defp validate_secret_key_base(nil), do:
raise(ArgumentError, "cookie store expects conn.secret_key_base to be set")
defp validate_secret_key_base(secret_key_base) when byte_size(secret_key_base) < 64, do:
raise(ArgumentError, "cookie store expects conn.secret_key_base to be at least 64 bytes")
defp validate_secret_key_base(secret_key_base), do:
secret_key_base
defp check_signing_salt(opts) do
case opts[:signing_salt] do
nil -> raise ArgumentError, "cookie store expects :signing_salt as option"
salt -> salt
end
end
defp check_serializer(serializer) when is_atom(serializer), do: serializer
defp check_serializer(_), do:
raise(ArgumentError, "cookie store expects :serializer option to be a module")
end