security-patterns
Elixir-specific security patterns, OWASP mitigations, and compliance best practices
$ Installieren
git clone https://github.com/layeddie/ai-rules /tmp/ai-rules && cp -r /tmp/ai-rules/skills/security-patterns ~/.claude/skills/ai-rules// tip: Run this command in your terminal to install the skill
name: security-patterns description: Elixir-specific security patterns, OWASP mitigations, and compliance best practices
Security Patterns Skill
Use this skill when implementing security features, handling user data, or ensuring compliance in Elixir applications.
When to Use
- Implementing authentication and authorization
- Handling user input and validation
- Working with sensitive data (passwords, tokens)
- Designing secure APIs
- Ensuring GDPR/SOC2/HIPAA compliance
- Implementing OWASP Top 10 mitigations
- Managing secrets and encryption
- Preventing common security vulnerabilities
OWASP Top 10 Mitigations
A01: Broken Access Control
Problem: Users can access resources they shouldn't.
Elixir Solution: Role-Based Access Control (RBAC) with Ash policies
defmodule Accounts.Policies.User do
use Ash.Policy
policy :read do
authorize_if user.id == resource.user_id
end
policy :update do
authorize_if user.id == resource.user_id and user.role in [:admin, :manager]
end
end
A02: Cryptographic Failures
Problem: Using weak or deprecated cryptography.
Elixir Solution: Use proven crypto libraries
# â
Good: Argon2 for password hashing
def hash_password(password) do
Argon2.hash_pwd(password)
end
def verify_password(password, hash) do
Argon2.verify_pwd(password, hash)
end
# â Bad: SHA1 or MD5
def hash_password_legacy(password) do
:crypto.hash(:sha256, password)
end
A03: Injection
Problem: SQL injection via string interpolation.
Elixir Solution: Parameterized queries with Ecto
# â
Good: Parameterized
def get_user(id) do
from(u in User, where: u.id == ^id)
|> Repo.one()
end
# â Bad: String interpolation
def get_user_unsafe(id) do
Repo.query!("SELECT * FROM users WHERE id = #{id}")
end
A04: Insecure Design
Problem: Security through obscurity or unvalidated redirects.
Elixir Solution: Proper session management and secure redirects
# â
Good: Constant-time comparison
def verify_token(token, stored_token) do
Plug.Crypto.secure_compare(token, stored_token)
end
# â Bad: Timing-sensitive comparison
def verify_token_unsafe(token, stored_token) do
token == stored_token
end
A07: Identification and Authentication Failures
Problem: Weak session management.
Elixir Solution: Secure JWT with proper signing and verification
defmodule Auth.Token do
use Joken
def generate_token(user_id, expiration \\ 3600) do
signer()
|> add_claim("sub", user_id)
|> add_claim("exp", current_time() + expiration)
|> sign()
end
def verify_token(token) do
verifier()
|> verify(token)
end
end
A08: Software and Data Integrity Failures
Problem: Using unsigned packages or no checksums.
Elixir Solution: Pin dependencies and verify checksums
# mix.exs
defp deps do
[
{:bcrypt_elixir, "~> 3.0"},
{:jason, "~> 1.4"},
{:ash, "~> 3.0", override: true} # Pin to specific version
]
end
A09: Security Logging and Monitoring
Problem: Logging sensitive data or poor error handling.
Elixir Solution: Structured logging with redaction
# â
Good: Redact sensitive data
def log_user_action(user_id, action) do
Logger.info("User action", user_id: anonymize(user_id), action: action)
end
defp anonymize(user_id) do
"usr_#{Base.encode16(:crypto.hash(:md5, user_id))}"
end
# â Bad: Log sensitive data
def log_user_action_unsafe(user_id, action) do
IO.inspect("User #{user_id} performed #{action}")
end
Elixir-Specific Security
Atom Exhaustion Protection
Problem: Creating atoms from user input can exhaust atom table.
Elixir Solution: Use binaries or limit atom creation
# â
Good: Use binaries
def handle_user_type(type) when is_binary(type) do
# type is already a binary
:ok
end
def handle_user_type(type) do
# Convert to atom ONLY for known values
case type do
"admin" -> :admin
"user" -> :user
_ -> :unknown
end
end
# â Bad: Direct atom creation
def handle_user_type_unsafe(type) do
String.to_atom(type)
end
Timing Attack Prevention
Problem: Variable-time operations can leak information.
Elixir Solution: Constant-time operations
# â
Good: Constant-time comparison
def compare_hashes(hash1, hash2) do
Plug.Crypto.secure_compare(hash1, hash2)
end
# â Bad: Linear-time comparison
def compare_hashes_unsafe(hash1, hash2) do
:crypto.hash(:sha256, hash1) == :crypto.hash(:sha256, hash2)
end
Boolean Coercion Prevention
Problem: Truthy/falsy values causing security issues.
Elixir Solution: Explicit boolean checks
# â
Good: Explicit checks
def has_permission?(user, permission) do
permission in user.permissions
end
# â Bad: Relies on truthiness
def has_permission_unsafe(user, permission) do
user.permissions[permission]
end
Cookie Security
Secure Cookie Attributes
# â
Good: Secure cookie configuration
def secure_session(conn) do
conn
|> put_resp_cookie("session_id", session_id,
max_age: 3600,
secure: true, # HTTPS only
http_only: true, # Not accessible via JavaScript
same_site: "Strict", # CSRF protection
path: "/",
encrypt: true
)
end
Input Validation
Ash Changesets for Security
defmodule Accounts.User do
use Ash.Resource
attributes do
attribute :email, :string,
allow_nil?: false,
constraints: [
max_length: 255,
match: ~r/^[\w-\.]+@[\w-\.]+$/,
format: [with: ~r/@/]
]
attribute :password, :string,
allow_nil?: false,
constraints: [
min_length: 12,
max_length: 128,
format: [with: ~r/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/]
]
end
end
CSRF Protection
Phoenix Plug Configuration
defmodule MyAppWeb.Plugs.CSRFProtection do
import Plug.Conn
alias Phoenix.Controller
def init(opts), do: opts
def call(conn, opts) do
# Check CSRF token for state-changing requests
if conn.method in ["POST", "PUT", "PATCH", "DELETE"] do
check_csrf_token(conn, opts)
else
conn
end
end
defp check_csrf_token(conn, opts) do
token = get_csrf_token(conn)
case verify_csrf_token(token, conn) do
:ok -> conn
:error ->
conn
|> put_status(403)
|> halt()
end
end
end
Secrets Management
Environment Variables
# config/runtime.exs
import Config
config :my_app, :secrets,
database_url: System.get_env("DATABASE_URL"),
secret_key_base: System.get_env("SECRET_KEY_BASE")
# NEVER hardcode secrets
# â config/config.exs
# config :my_app, :secrets,
# secret_key: "hardcoded_secret_key_here" # DANGEROUS!
Encryption at Rest
# â
Good: Database encryption
def encrypt_sensitive_data(data) do
:crypto.block_encrypt(
:aes_gcm,
Application.get_env(:my_app, :encryption_key),
data,
true # Base64 encoding
)
end
# â Bad: Storing plaintext
def store_sensitive_data(data) do
Repo.insert(%Sensitive{data: data}) # NO ENCRYPTION!
end
Compliance Frameworks
GDPR Compliance Checklist
- Data minimization: collect only necessary data
- Right to erasure: user data deletion on request
- Data portability: export user data in standard format
- Explicit consent: opt-in for data processing
- Data breach notification: 72-hour rule
- Privacy by design: privacy impact assessment
- Data protection officer: DPO assigned
- Data retention policy: defined time limits
- Consent management: granular permissions
SOC2 Compliance Checklist
- Access control: least privilege principle
- Audit logging: all security-relevant actions logged
- Network security: TLS/HTTPS everywhere
- Change management: controlled changes
- System monitoring: real-time alerts
- Incident response: procedures defined
- Penetration testing: regular security tests
- Vulnerability management: tracking and remediation
Security Tools
Sobelow (Static Analysis)
# Install
mix deps.get sobelow
# Run
mix sobelow --verbose
# Common findings
# - High: XSS vulnerability
# - High: SQL injection
# - Medium: CSRF protection missing
Dependency Scanning
# Mix Hex Audit
mix hex.outdated
mix hex.audit --format json
# Continuous scanning in CI/CD
# - Automated security checks on every PR
# - Automated dependency updates
Secrets Detection
# Git Secrets scanning
git secrets --scan
# Detects:
# - API keys
# - Database passwords
# - Private keys
# - Access tokens
Anti-Patterns
Security Through Obscurity
# â Anti-pattern: Hiding security issues
# defmodule SecretAPI do
# # Secret algorithm (NO!)
# def encrypt(data) do
# :crypto.hash(:md5, data)
# end
# end
# â
Pattern: Use proven, audited libraries
defmodule SecretAPI do
def encrypt(data) do
Argon2.hash_pwd(data)
end
end
Frontend Authorization Checks
# â Anti-pattern: Authorization checks in UI
# # Phoenix LiveView
# def mount(%{assigns: %{current_user: user}}) do
# if user.role != :admin do
# push_redirect("/forbidden")
# end
# end
# â
Pattern: Backend-only authorization
defmodule Accounts.Policies.Admin do
policy :admin_only do
authorize_if user.role == :admin
end
end
Best Practices Summary
Authentication & Authorization
- Use Ash policies for authorization, never in controllers
- Implement JWT with proper signing and expiration
- Store session data in server, not client
- Use CSRF tokens for state-changing requests
- Enable rate limiting on authentication endpoints
Data Protection
- Never log sensitive data (passwords, tokens, PII)
- Encrypt at rest using strong encryption (AES-256-GCM)
- Use TLS everywhere (HTTPS for all endpoints)
- Validate input at application boundaries
- Use parameterized queries (Ecto) to prevent injection
Secrets Management
- Use environment variables for all secrets
- Never commit secrets to version control
- Rotate secrets regularly
- Use secret scanning in CI/CD pipelines
- Implement secret injection for development
OWASP Protection
- Implement RBAC with Ash policies
- Prevent injection via parameterized queries
- Use strong cryptography (Argon2, bcrypt)
- Implement CSRF protection everywhere
- Validate and sanitize all user input
- Use secure headers (CSP, HSTS, X-Frame-Options)
Compliance
- Document data processing activities
- Implement data export/deletion on request
- Maintain audit logs for compliance evidence
- Conduct regular security assessments
- Document incident response procedures
Tools to Use
- Sobelow: Security static analysis for Elixir
- Hex.audit: Dependency vulnerability scanning
- Git-secrets: Secrets detection in repositories
- Plug.Crypto: Constant-time crypto operations
- Ash.Policy: Authorization policies
- Joken: JWT token management
- Phoenix.LiveSecurity: LiveView security patterns
Resources
- OWASP Top 10: https://owasp.org/www-project-top-ten
- Elixir Secure Coding Training: https://github.com/erlef/elixir-secure-coding
- CWE Top 25: https://cwe.mitre.org/top25/
- Elixir Security Guidelines: https://hexdocs.pm/phoenix/plug_security
Repository
