按框架
Ruby on Rails 安全 — 生产环境加固实务参考
Ruby on Rails 生产环境加固参考:优先级清单,外加密钥/credentials(master key/secret_key_base)、配置、gem CVE、Strong Parameters、授权、注入与 SSRF。以防御为主,不含攻击步骤。
面向:在 Ruby on Rails 上运营应用的人。这里不讲攻击步骤——这是一份用于加固的实务参考:优先级排序的清单、分领域指引、以及自查。要看跨框架的全局,请参阅 按框架的安全防护入口。
优先级排序的加固清单
按此表自上而下执行。P0 是前提,P1 是最频繁的事故来源,P2 是持续的运营卫生。
P0 ── 前提(先做这个)
密钥与 credentials 管理 / 生产环境配置(不暴露异常、force_ssl)/ 快速为 gem CVE 打补丁
P1 ── 最主要的事故来源
Strong Parameters/Mass Assignment 控制 / 授权(所有者范围)
P2 ── 运营卫生
注入与危险方法 / 会话、CSRF / SSRF、上传
| 优先级 | 控制项 | 具体做法(Rails) |
|---|---|---|
| P0 | 密钥与 credentials | 加密的 credentials + 分离的 master key。不要提交 master key。secret_key_base 泄露时轮换 |
| P0 | 生产环境配置 | 不暴露异常详情;config.force_ssl;用 filter_parameters 让密钥不进日志 |
| P0 | gem(依赖)CVE | 用 bundler-audit / osv-scanner 监控;以运行中的版本判定,快速打补丁 |
| P1 | Strong Parameters | 把 permit 控制到最少。不要用 permit!。绝不赋值权限字段 |
| P1 | 显式授权 | Pundit / CanCanCan + authorize + 按 current_user 限定范围以确认所有权 |
| P2 | 注入/危险方法 | 在 where 中绑定。不要把输入传给 send/constantize。不要加载不受信任的 YAML/Marshal |
| P2 | 会话/CSRF | protect_from_forgery(默认)、Cookie 的 secure/httponly/samesite、登录时重新生成 |
| P2 | SSRF/上传 | 服务端取回用允许列表 + 阻断内部 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 真的加固了吗
做完不算完——检查过才算完成。以下是针对你自己环境的防御性自查。
密钥没有暴露/被提交
master.key 不在仓库里,且 .env/转储无法通过 URL 取到。生产环境不暴露异常详情
授权是否有效
依赖与响应头
bundler-audit/osv 干净,并通过响应头检查器确认 HTTPS/HSTS 存在。本站的视角:即便有约定守护,授权和依赖仍归你自己
Rails 良好的默认值消除了大量风险,但授权——谁可以做什么,以及依赖的新鲜度,是与应用和运营强相关的,框架无法自动守护它们。我们反复看到的事故,与其说是精巧的攻击,不如说是“已认证但没做所有权检查”这一类。所以重心在于把上表自上而下逐条落实——收紧 Strong Parameters、让授权显式化、并监控 gem 的 CVE。
接下来读
- 入口:按框架的安全防护 · Laravel 安全(同为 MVC,授权形态相似)
- 实操:漏洞应对实务 · 监控依赖包的 CVE · 不要把密钥放进公开目录
- 术语:IDOR 是什么 · SQL 注入 · CSRF · SSRF
- 工具:安全响应头检查器
FAQ
Q要保护 Rails,第一步该做什么?
P0 的三件事:(1) 安全地处理密钥(把加密的 credentials 与 master key 分离,不要提交 master key;secret_key_base 是签名/加密 Cookie 的根基,泄露时要轮换);(2) 固化生产环境配置(不暴露异常详情;force_ssl);(3) 用机器监控 gem(依赖)CVE 并快速打补丁,以运行中的版本判定。接着再进入 Strong Parameters 与授权。
Q使用 Strong Parameters 要注意什么?
显式允许(permit)你接受哪些字段,以防止 Mass Assignment(把本不该赋值的字段批量赋值)。危险在于为图省事而宽泛地 permit,以及用 permit! 允许一切。请把允许的字段控制到最少,绝不要让 is_admin 这类权限字段由用户输入赋值。
Q怎样才能安全地实现授权?
在登录(认证)之上,实现所有权检查,确认对象确实属于该用户。用 Pundit/CanCanCan 把策略写清楚,在每个 action 里都跑 authorize,并按 current_user 限定资源范围。不要依赖隐藏路由或难以猜测的 ID——在每一条读取/更新/删除路径上都显式核验。
Q如何管理 gem(依赖)漏洞?
用 bundler-audit 或 osv-scanner 机器监控已知 CVE,并以运行中的版本判定(依据 Gemfile.lock 里的实际版本),快速打补丁。让 Rails 和 Ruby 保持在受支持的版本,不要放着 EOL 版本不管。依赖的新鲜度,比精巧的攻击更能左右事故的成败。
Q有哪些危险的动态方法?
把用户输入传给 send / public_send / constantize,可能导致意料之外的方法调用或类解析。同样,通过 Marshal 或不受信任的 YAML 加载(还原对象),在特定条件下可能导致代码执行。不要把用户来源的数据传给这些方法;若必须传,请用允许列表严格限制。