Elixir/Phoenix Security Reference
Quick reference for security patterns in Elixir/Phoenix.
Iron Laws — Never Violate These
-
VALIDATE AT BOUNDARIES — Never trust client input. All data through changesets
-
NEVER INTERPOLATE USER INPUT — Use Ecto's ^ operator, never string interpolation
-
NO String.to_atom WITH USER INPUT — Atom exhaustion DoS. Use to_existing_atom/1
-
AUTHORIZE EVERYWHERE — Check in contexts AND re-validate in LiveView events
-
ESCAPE BY DEFAULT — Never use raw/1 with untrusted content
-
SECRETS NEVER IN CODE — All secrets in runtime.exs from env vars
Quick Patterns
Timing-Safe Authentication
def authenticate(email, password) do user = Repo.get_by(User, email: email)
cond do user && Argon2.verify_pass(password, user.hashed_password) -> {:ok, user} user -> {:error, :invalid_credentials} true -> Argon2.no_user_verify() # Timing attack prevention {:error, :invalid_credentials} end end
LiveView Authorization (CRITICAL)
RE-AUTHORIZE IN EVERY EVENT HANDLER
def handle_event("delete", %{"id" => id}, socket) do post = Blog.get_post!(id)
Don't trust that mount authorized this action!
with :ok <- Bodyguard.permit(Blog, :delete_post, socket.assigns.current_user, post) do Blog.delete_post(post) {:noreply, stream_delete(socket, :posts, post)} else _ -> {:noreply, put_flash(socket, :error, "Unauthorized")} end end
SQL Injection Prevention
✅ SAFE: Parameterized queries
from(u in User, where: u.name == ^user_input)
❌ VULNERABLE: String interpolation
from(u in User, where: fragment("name = '#{user_input}'"))
Quick Decisions
What to validate?
-
All user input → Ecto changesets
-
File uploads → Extension + magic bytes + size
-
Paths → Path.safe_relative/2 for traversal
-
Atoms → String.to_existing_atom/1 only
What to escape?
-
HTML output → Auto-escaped by default (<%= %> )
-
User HTML → HtmlSanitizeEx with scrubber
-
Never → raw/1 with untrusted content
Anti-patterns
Wrong Right
"SELECT * FROM users WHERE name = '#{name}'"
from(u in User, where: u.name == ^name)
String.to_atom(user_input)
String.to_existing_atom(user_input)
<%= raw @user_comment %>
<%= @user_comment %>
Hardcoded secrets in config runtime.exs from env vars
Auth only in mount Re-auth in every handle_event
References
For detailed patterns, see:
-
references/authentication.md
-
phx.gen.auth, MFA, sessions
-
references/authorization.md
-
Bodyguard, scopes, LiveView auth
-
references/input-validation.md
-
Changesets, file uploads, paths
-
references/security-headers.md
-
CSP, CSRF, rate limiting, headers
-
references/oauth-linking.md
-
OAuth account linking, token management
-
references/rate-limiting.md
-
Composite key strategies, Hammer patterns
-
references/advanced-patterns.md
-
SSRF prevention, secrets management, supply chain