Rails和HTTP422那点事儿

Rails和HTTP422那点事儿

发布于2020-08-12 22:07:02 UTC  2

1. 问题描述

Ruby on Rails 开发点接口的时候遇到了HTTP422问题。回去看了眼公司的项目,发现在 environments 里写了一句这个:

config.action_controller.allow_forgery_protection = false

参考了一下 Ruby on Rails 文档中对于 csrf 的描述:

默认情况下,Rails 自带的非侵入式脚本适配器会在每个非 GET Ajax 调用中添加名为 X-CSRF-Token 的首部,其值为安全令牌。如果没有这个首部,Rails 不会接受非 GET Ajax 请求。使用其他库调用 Ajax 时,同样要在默认首部中添加 X-CSRF-Token。要想获取令牌,请查看应用视图中由 <%= csrf_meta_tags %> 这行代码生成的 标签。 除了指定的接口外,都需要认证。 ——Ruby on Rails 安全指南

上述配置是非常不推荐的。

2. 解决方案

CSRF又称为跨站点请求伪造攻击,大意是通过一些手段骗取用户的cookie来向源站点发起请求,以达到在用户浑然不觉的情况下使用其账户进行操作的攻击,也是极为常见的一种Web安全问题。

Rails对于CSRF的防御策略是在每个页面上生成一个只有服务器才知道的token用于验证发起请求的站点究竟是不是允许请求的站点,当服务器验证token失败时,会返回HTTP422拒绝本次请求。 当我们在Rails里编写表单提交时,应该尽可能的避免编写原生表单,而是应该使用如下代码代替之:

<%= form_for @user do |f| %>
  <%= f.text_field :username %>
  <%= f.text_field :password %>
<% end %>

这是Rails的View Helper方法,使用该段代码生成的表单将自动添加验证token,开发者不必额外关注即可解决安全隐患。 在部分手动提交表单(如ajax)的场景里,我们可以使用form_authenticity_token方法获取到token:

<form action="/sessions" method="post">
  <input type='hidden' value="<%= form_authenticity_token %>" name="authenticity_token"/>
  <input name='username' type="text"/>
  <input name='password' type="password"/>
  <input type="submit"/>
</form>

现代前后端分离的架构下我们通常使用如jwt令牌等token来标记请求状态,因此在Api开发的场景下我们可以酌情考虑关闭CSRF的验证机制(具体可参考如下代码)。

但仍需要注意一点是,token的存储问题是值得考虑的,存储在cookie中则依然无法避免CSRF漏洞,反之如果存储在LocalStorage中,则需要留意XSS攻击的防御。

class ApplicationController < ActionController::Base
  skip_before_action :verify_authenticity_token, if: :json_request?

  def json_request?
    request.format.json?
  end
end

以下是已经废弃的原内容

2. 解决方案

2.1 使用 ActionController::Base 时的细节

该问题通常可能出现在使用 ActionController::Base 作为 Controller 处理器,但却自己构建了请求的场景。正如上述文档提供的描述,我们应该在表单中构建对应的字段传递 X-CSRF-Token 作为请求头来规避该问题。 如果你实在希望关闭该机制,也可以使用如下黑名单的方式来对使指定的接口跳过认证:

protect_from_forgery :except => :actions

或者使用白名单方式来限定需要认证的接口:

protect_from_forgery :only=> :actions

2.2 使用 ActionController::API

虽然说来可能有点可笑,但确实存在部分仅提供 API 的应用还在使用 ActionController::Base 作为基类,这样会带来一些无意义的损耗,您应该将其进行替换:

class ApiController < ActionController::API
end

评论

共有 0 条评论

作者的其他博客