authority_ecto v0.1.2 Authority.Ecto.HMAC
An Ecto.Type
to hash secrets (like tokens) using HMAC to reduce the
chance of leaking, even if your database is compromised.
If reversible encryption is desired, see the
Cloak library instead.
Relies on :crypto.hmac/3
, using the hash algorithm :sha256
.
Configuration
Define an HMAC
module in your project:
defmodule MyApp.HMAC do
use Authority.Ecto.HMAC, secret: "secret here"
end
The :secret
is required and can be specified any of the following ways:
# A simple string
secret: "string"
# An environment variable
secret: {:system, "VARIABLE_NAME"}
# An application configuration value
secret: {:app_env, :my_app, :hmac_secret}
# An application module configuration value
secret: {:app_env, :my_app_web, MyAppWeb.Endpoint, :secret}
If fetching from the :app_env
, :system
tuples are still supported.
config :my_app, hmac_secret: {:system, "HMAC_SECRET"}
Once your HMAC
field module is configured, change your secret field’s type
from :string
to MyApp.HMAC
.
schema "tokens" do
field :token, MyApp.HMAC
end
Next, update your changeset function to generate random token strings using
Authority.Ecto.Changeset.put_token/2
.
import Authority.Ecto.Changeset
def changeset(struct, attrs \ %{}) do
struct
|> cast(attrs, [...])
|> put_token(:token)
end
Usage
The :token
field will now automatically generate and hash using the
specified secret.
This means that the original token value is only available immediately after inserting the record. You should immediately store the original on the client-side. When the token is fetched from the database later, only the irreversible hashed value will be returned.
# The original, raw value is available after insert
token =
%Token{}
|> Token.changeset()
|> Repo.insert!
# => %Token{token: "original-value"}
# Subsequent loads will only return a hashed value
Repo.get!(Token, token.id)
# => %Token{token: "4F5410A9D48AD80826A027F98DC7B9E1D20E5C42D3E7F341549954C28B5ABA89"}
However, if you preserved the original value on the client-side, (such as in a session cookie) you can still query using it. Ecto will transparently hash the value before using it to query the database.
Token
|> where(token: "original-value")
|> Repo.one()
# => %Token{token: "4F5410A9D48AD80826A027F98DC7B9E1D20E5C42D3E7F341549954C28B5ABA89"}
Link to this section Summary
Functions
Hashes a given string using the given secret, and returns the value as a
Base.encode16/1
string. Relies on :crypto.hmac/3
with hash algorithm
:sha256
Link to this section Functions
Hashes a given string using the given secret, and returns the value as a
Base.encode16/1
string. Relies on :crypto.hmac/3
with hash algorithm
:sha256
.
Examples
iex> hash("hello world", "authority")
"0695772E021A6880A6EF0D26C4898E733A1923A7CDBD5150F5B0C38B788A76E8"