-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate and validate a CSRF token for POST and PUT requests
- Loading branch information
Showing
1 changed file
with
73 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
defmodule Aino.Session.CSRF do | ||
@moduledoc """ | ||
Session Middleware for handling CSRF validation | ||
Example for using CSRF: | ||
```elixir | ||
middleware = [ | ||
Aino.Middleware.common(), | ||
&Aino.Session.config(&1, %Aino.Session.Cookie{key: "key", salt: "salt"}), | ||
&Aino.Session.decode/1, | ||
&Aino.Session.Flash.load/1, | ||
&Aino.Middleware.Routes.routes(&1, routes()), | ||
&Aino.Middleware.Routes.match_route/1, | ||
&Aino.Middleware.params/1, | ||
&Aino.Session.CSRF.validate/1, | ||
&Aino.Session.CSRF.generate/1, | ||
&Aino.Middleware.Routes.handle_route/1, | ||
&Aino.Session.encode/1, | ||
&Aino.Middleware.logging/1 | ||
] | ||
Aino.Token.reduce(token, middleware) | ||
``` | ||
`validate/1` and `generate/1` should be after `Session.load/1` and | ||
`Aino.Middleware.params/1` to make sure the token can be properly loaded. | ||
Your forms must now include a new hidden field named `_csrf_token`. This will | ||
use the session's `_csrf_token` value. | ||
``` | ||
<input type="hidden" name="_csrf_token" value="<%= @token.session["_csrf_token"] %>" /> | ||
``` | ||
""" | ||
|
||
alias Aino.Session | ||
|
||
require Logger | ||
|
||
@doc """ | ||
Validate the token is present if a POST or PUT | ||
""" | ||
def validate(token) do | ||
if token.request.method in [:POST, :PUT] do | ||
# if the token isn't present in either, reject the request | ||
# if the provided token doesn't match, reject the request | ||
|
||
session_token = token.session["_csrf_token"] | ||
request_token = token.params["_csrf_token"] | ||
|
||
if present?(session_token) && present?(request_token) && session_token == request_token do | ||
token | ||
else | ||
raise "Invalid CSRF token" | ||
end | ||
else | ||
token | ||
end | ||
end | ||
|
||
defp present?(token) do | ||
token != nil && token != "" | ||
end | ||
|
||
@doc """ | ||
Generate a token and store in the session | ||
""" | ||
def generate(token) do | ||
csrf_token = :crypto.strong_rand_bytes(32) |> Base.encode64(padding: false) | ||
Session.Token.put(token, "_csrf_token", csrf_token) | ||
end | ||
end |