<< All versions
Skill v1.0.1
currentAutomated scan100/100oliver-kriska/claude-elixir-phoenix/security
10 files
──Details
PublishedJuly 3, 2026 at 08:34 AM
Content Hashsha256:335f74b8b0880985...
Git SHAfa20999dbbff
Bump Typepatch
──Files
Files (1 file, 3.7 KB)
SKILL.md3.7 KBactive
SKILL.md · 111 lines · 3.7 KB
version: "1.0.1" name: security description: "Enforce Elixir/Phoenix security — auth, OAuth, sessions, CSRF, XSS, SQL injection, input validation, secrets. Use when editing auth files, login flows, RBAC, or API keys." effort: medium user-invocable: false paths:
- "**/auth.ex"
- "**/session.ex"
- "**/password.ex"
Elixir/Phoenix Security Reference
Ash projects:AshAuthenticationhas its own strategy/token patterns — use theash-frameworkskill. CSRF, XSS, and secret management patterns below still apply.
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/1with untrusted content - SECRETS NEVER IN CODE — All secrets in
runtime.exsfrom env vars
Quick Patterns
Timing-Safe Authentication
elixir
def authenticate(email, password) douser = Repo.get_by(User, email: email)cond douser && Argon2.verify_pass(password, user.hashed_password) ->{:ok, user}user ->{:error, :invalid_credentials}true ->Argon2.no_user_verify() # Timing attack prevention{:error, :invalid_credentials}endend
LiveView Authorization (CRITICAL)
elixir
# RE-AUTHORIZE IN EVERY EVENT HANDLERdef handle_event("delete", %{"id" => id}, socket) dopost = Blog.get_post!(id)# Don't trust that mount authorized this action!with :ok <- Bodyguard.permit(Blog, :delete_post, socket.assigns.current_user, post) doBlog.delete_post(post){:noreply, stream_delete(socket, :posts, post)}else_ -> {:noreply, put_flash(socket, :error, "Unauthorized")}endend
SQL Injection Prevention
elixir
# ✅ SAFE: Parameterized queriesfrom(u in User, where: u.name == ^user_input)# ❌ VULNERABLE: String interpolationfrom(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/2for traversal - Atoms →
String.to_existing_atom/1only
What to escape?
- HTML output → Auto-escaped by default (
<%= %>) - User HTML → HtmlSanitizeEx with scrubber
- Never →
raw/1with 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:
${CLAUDE_SKILL_DIR}/references/authentication.md- phx.gen.auth, MFA, sessions${CLAUDE_SKILL_DIR}/references/authorization.md- Bodyguard, scopes, LiveView auth${CLAUDE_SKILL_DIR}/references/input-validation.md- Changesets, file uploads, paths${CLAUDE_SKILL_DIR}/references/security-headers.md- CSP, CSRF, rate limiting, headers${CLAUDE_SKILL_DIR}/references/oauth-linking.md- OAuth account linking, token management${CLAUDE_SKILL_DIR}/references/rate-limiting.md- Composite key strategies, Hammer patterns${CLAUDE_SKILL_DIR}/references/advanced-patterns.md- SSRF prevention, secrets management, supply chain