跳到正文
>_ITDITDWeb 安全平台

按框架

Ruby on Rails 安全 — 生产环境加固实务参考

Ruby on Rails 生产环境加固参考:优先级清单,外加密钥/credentials(master key/secret_key_base)、配置、gem CVE、Strong Parameters、授权、注入与 SSRF。以防御为主,不含攻击步骤。

发布于 2026-07-02 更新于 2026-07-02 3 分钟阅读

面向:在 Ruby on Rails 上运营应用的人。这里不讲攻击步骤——这是一份用于加固的实务参考:优先级排序的清单、分领域指引、以及自查。要看跨框架的全局,请参阅 按框架的安全防护入口

优先级排序的加固清单

按此表自上而下执行。P0 是前提,P1 是最频繁的事故来源,P2 是持续的运营卫生。

P0 ── 前提(先做这个)

密钥与 credentials 管理 / 生产环境配置(不暴露异常、force_ssl)/ 快速为 gem CVE 打补丁

P1 ── 最主要的事故来源

Strong Parameters/Mass Assignment 控制 / 授权(所有者范围)

P2 ── 运营卫生

注入与危险方法 / 会话、CSRF / SSRF、上传

从地基往上加固:P0(前提)→ P1(最主要的事故来源)→ P2(运营卫生)。
优先级控制项具体做法(Rails)
P0密钥与 credentials加密的 credentials + 分离的 master key。不要提交 master keysecret_key_base 泄露时轮换
P0生产环境配置不暴露异常详情;config.force_ssl;用 filter_parameters 让密钥不进日志
P0gem(依赖)CVE用 bundler-audit / osv-scanner 监控;以运行中的版本判定,快速打补丁
P1Strong Parameterspermit 控制到最少。不要用 permit!。绝不赋值权限字段
P1显式授权Pundit / CanCanCan + authorize + 按 current_user 限定范围以确认所有权
P2注入/危险方法where 中绑定。不要把输入传给 send/constantize。不要加载不受信任的 YAML/Marshal
P2会话/CSRFprotect_from_forgery(默认)、Cookie 的 secure/httponly/samesite、登录时重新生成
P2SSRF/上传服务端取回用允许列表 + 阻断内部 IP。校验上传,存放在 public/ 之外

1. 密钥与 credentials(P0)

  • 使用 Rails 的加密 credentials,并且不要把 master(解密)密钥提交到仓库(通过环境变量等注入)。
  • secret_key_base 是签名/加密 Cookie 和会话的根基。一旦泄露就及时轮换(注意这会使既有的签名/加密失效)。
  • 不要把 .env、转储或备份放在公开目录里(→ 不要把密钥放进公开目录)。

2. 生产环境配置(P0)

  • 在生产环境不要暴露异常详情(不要启用 consider_all_requests_local 等)——减少内部结构的泄露。
  • config.force_ssl 强制 HTTPS 并把 Cookie 标记为 secure。不要在生产环境启用 web console 等开发工具。
  • filter_parameters 让密码之类不进日志。

3. gem(依赖)CVE(P0)

  • bundler-audit 或 osv-scanner 机器监控 gem 的 CVE,以运行中的版本(Gemfile.lock)判定,并快速打补丁(→ 监控依赖包的 CVE · 漏洞应对实务)。
  • Rails 和 Ruby 保持在受支持的版本,不要放着 EOL 版本不管。

4. Strong Parameters 与 Mass Assignment(P1)

  • permit 的字段控制到最少,避免 permit!(允许一切)。
  • **绝不要让 is_admin 这类权限字段由用户输入赋值。**放弃“图省事就宽泛地 permit”。

5. 授权(P1 — 最主要的来源)

常见(危险)

  • 没有授权——“已登录=能看/能改”
  • 路由模型绑定取到了别的用户的 ID
  • 依赖隐藏路由 / 难以猜测的 ID
  • 在某些 action 上忘了 authorize

正确

  • Pundit / CanCanCan 把策略写清楚,并在每个 action 里 authorize
  • 读取也按 current_user所有者范围限定
  • 每一条读取/更新/删除路径上核验
  • 授权显式构建,不交给默认值

参阅 IDOR 是什么。认证和授权是两回事;在靠近数据的地方做授权。

6. 注入与危险的动态方法(P2)

  • SQL:在 where用占位符绑定;绝不要用字符串拼接构造查询(避免 where("... #{params}"))(→ SQL 注入是什么)。
  • 危险的动态方法不要把用户输入传给 send/public_send/constantize。若必须传,请用允许列表严格限制。
  • 反序列化:避免加载不受信任的 YAML/Marshal(在特定条件下可能导致代码执行)。

7. 会话、Cookie、CSRF(P2)

  • 不要全局关闭 CSRF 防护(protect_from_forgery;用好表单令牌(→ CSRF 是什么)。
  • 给 Cookie/会话设置 secure/httponly/samesite,并在登录时重新生成会话。

8. SSRF、上传、响应头(P2)

  • 服务端取回用户提供的 URL 时,应把目标限制在允许列表,并阻断到内部 IP/元数据的访问(→ SSRF 是什么)。
  • 校验上传(类型/大小),存放在 public/ 之外,且不授予执行权限。
  • 加上安全响应头(用 安全响应头检查器 检查你自己的站点)。

验证:你的 Rails 真的加固了吗

做完不算完——检查过才算完成。以下是针对你自己环境的防御性自查。

1

密钥没有暴露/被提交

确认 master.key 不在仓库里,且 .env/转储无法通过 URL 取到。
2

生产环境不暴露异常详情

故意触发一个错误,确认详细异常不会对外显示,且 HTTPS 被强制。
3

授权是否有效

在测试环境里,请求别的用户的资源 ID,确认被拒绝(读取/更新/删除)。
4

依赖与响应头

确认 bundler-audit/osv 干净,并通过响应头检查器确认 HTTPS/HSTS 存在。

本站的视角:即便有约定守护,授权和依赖仍归你自己

Rails 良好的默认值消除了大量风险,但授权——谁可以做什么,以及依赖的新鲜度,是与应用和运营强相关的,框架无法自动守护它们。我们反复看到的事故,与其说是精巧的攻击,不如说是“已认证但没做所有权检查”这一类。所以重心在于把上表自上而下逐条落实——收紧 Strong Parameters、让授权显式化、并监控 gem 的 CVE。

接下来读

FAQ

Q要保护 Rails,第一步该做什么?
A

P0 的三件事:(1) 安全地处理密钥(把加密的 credentials 与 master key 分离,不要提交 master key;secret_key_base 是签名/加密 Cookie 的根基,泄露时要轮换);(2) 固化生产环境配置(不暴露异常详情;force_ssl);(3) 用机器监控 gem(依赖)CVE 并快速打补丁,以运行中的版本判定。接着再进入 Strong Parameters 与授权。

Q使用 Strong Parameters 要注意什么?
A

显式允许(permit)你接受哪些字段,以防止 Mass Assignment(把本不该赋值的字段批量赋值)。危险在于为图省事而宽泛地 permit,以及用 permit! 允许一切。请把允许的字段控制到最少,绝不要让 is_admin 这类权限字段由用户输入赋值。

Q怎样才能安全地实现授权?
A

在登录(认证)之上,实现所有权检查,确认对象确实属于该用户。用 Pundit/CanCanCan 把策略写清楚,在每个 action 里都跑 authorize,并按 current_user 限定资源范围。不要依赖隐藏路由或难以猜测的 ID——在每一条读取/更新/删除路径上都显式核验。

Q如何管理 gem(依赖)漏洞?
A

用 bundler-audit 或 osv-scanner 机器监控已知 CVE,并以运行中的版本判定(依据 Gemfile.lock 里的实际版本),快速打补丁。让 Rails 和 Ruby 保持在受支持的版本,不要放着 EOL 版本不管。依赖的新鲜度,比精巧的攻击更能左右事故的成败。

Q有哪些危险的动态方法?
A

把用户输入传给 send / public_send / constantize,可能导致意料之外的方法调用或类解析。同样,通过 Marshal 或不受信任的 YAML 加载(还原对象),在特定条件下可能导致代码执行。不要把用户来源的数据传给这些方法;若必须传,请用允许列表严格限制。