关于 security
面向 Elixir 和 Phoenix 开发者的安全参考,汇总输入验证、授权、转义、密钥管理与注入风险防护要点,也涵盖 LiveView 事件检查。
主要功能
- 01使用 changeset 在边界验证
- 02参数化 Ecto 查询模式
- 03LiveView 事件授权
- 04安全转义与 Atom 处理
- 05运行时密钥管理
适用场景
- 01检查用户输入处理方式
- 02保护 LiveView 事件处理器
- 03防范 SQL 注入与 Atom 耗尽
- 04检查密钥存储和 HTML 转义
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ oliver-kriska/ claude-elixir-phoenix --skill security安装后在 Claude Code 中输入/security确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/advanced-patterns.mdauthentication.mdauthorization.mdinput-validation.mdoauth-linking.mdrate-limiting.mdsecurity-headers.md详细介绍
Elixir/Phoenix 安全参考
Ash 项目:
AshAuthentication有自己的策略/令牌模式——请使用ash-framework技能。以下 CSRF、XSS 和密钥管理模式仍然适用。
Elixir/Phoenix 安全模式速查。
铁律——绝不可违反
- 在边界处验证——绝不信任客户端输入。所有数据都必须通过 changeset
- 绝不插入用户输入——使用 Ecto 的
^运算符,绝不进行字符串插值 - 绝不对用户输入使用 String.to_atom——这会导致 Atom 耗尽型 DoS。请使用
to_existing_atom/1 - 在所有位置进行授权——在上下文中检查,并在 LiveView 事件中重新验证
- 默认转义——绝不对不可信内容使用
raw/1 - 代码中绝不包含密钥——所有密钥都应从环境变量加载到
runtime.exs - LiveView 事件参数不可信——用户可以在 DevTools 中修改表单、钩子以及所有
phx-value-*。操作前,请根据服务器端状态进行验证和授权
常用模式
时序安全的身份验证
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 授权(重要)
# `id` is client input even when it came from phx-value-id.
# 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
渲染后的 LiveView 事件可能会在 HTML 和 WebSocket 载荷中暴露 ID。这不一定是漏洞:应将 ID 视为公开标识符,绝不可将其视为访问权限的证明。只有在标识符本身必须保密时才使用不透明引用,并且仍须在服务器端执行授权。
SQL 注入防护
# ✅ SAFE: Parameterized queries
from(u in User, where: u.name == ^user_input)
# ❌ VULNERABLE: String interpolation
from(u in User, where: fragment("name = '#{user_input}'"))
快速决策
应验证哪些内容?
- 所有用户输入 → Ecto changeset
- 文件上传 → 扩展名 + 魔数 + 大小
- 路径 → 使用
Path.safe_relative/2防止路径遍历 - Atom → 仅使用
String.to_existing_atom/1
应转义哪些内容?
- HTML 输出 → 默认自动转义(
<%= %>) - 用户 HTML → 使用带 scrubber 的 HtmlSanitizeEx
- 绝不 → 对不可信内容使用
raw/1
反模式
| 错误 | 正确 |
|---|---|
"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 %> |
| 在配置中硬编码密钥 | 从环境变量加载到 runtime.exs |
| 只在 mount 中进行身份验证 | 在每个 handle_event 中重新进行身份验证 |
信任 phx-value-* 或隐藏 ID | 从服务器端加载状态并进行授权 |
参考资料
详细模式请参阅:
${CLAUDE_SKILL_DIR}/references/authentication.md- phx.gen.auth、MFA、会话${CLAUDE_SKILL_DIR}/references/authorization.md- Bodyguard、作用域、LiveView 身份验证${CLAUDE_SKILL_DIR}/references/input-validation.md- Changeset、文件上传、路径${CLAUDE_SKILL_DIR}/references/security-headers.md- CSP、CSRF、速率限制、标头${CLAUDE_SKILL_DIR}/references/oauth-linking.md- OAuth 账号关联、令牌管理${CLAUDE_SKILL_DIR}/references/rate-limiting.md- 复合键策略、Hammer 模式${CLAUDE_SKILL_DIR}/references/advanced-patterns.md- SSRF 防护、密钥管理、供应链
评论
支持 Markdown