MST

星途 面试题库

面试题:Ruby on Rails 安全机制之 CSRF 防护原理及实现

解释 Ruby on Rails 中 CSRF(跨站请求伪造)防护的原理,说明 Rails 框架默认是如何实现 CSRF 防护的。如果需要自定义 CSRF 防护策略,你会从哪些方面入手,如何操作?
26.9万 热度难度
编程语言Ruby

知识考点

AI 面试

面试题答案

一键面试

CSRF 防护原理

CSRF 攻击利用用户已登录的身份,在用户不知情的情况下以用户的名义发送恶意请求。Rails 中 CSRF 防护原理是通过在每个表单和 AJAX 请求中添加一个独特的 CSRF 令牌(token)。服务器生成这个令牌并存储在用户的 session 中,当请求到达服务器时,服务器会检查请求中携带的 CSRF 令牌是否与 session 中存储的令牌一致。如果一致,则认为请求是合法的;如果不一致,则拒绝该请求,以此来防止 CSRF 攻击。

Rails 框架默认实现方式

  1. 表单:在 Rails 应用中,当使用 form_withform_tag 等帮助方法生成表单时,会自动在表单中添加一个隐藏的 csrf-token 字段,其值是从用户 session 中获取的唯一令牌。例如:
<form action="/some_path" method="post">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
  <!-- 其他表单字段 -->
</form>
  1. AJAX 请求:对于 AJAX 请求,Rails 通过 turbolinks 或手动设置等方式,会在请求头中添加 X-CSRF-Token 字段,其值同样是 session 中的 CSRF 令牌。在 Rails 应用的 JavaScript 环境中,rails-ujs 库会自动处理这个过程,确保 AJAX 请求携带正确的 CSRF 令牌。

自定义 CSRF 防护策略

  1. 令牌生成方式
    • 操作:可以自定义令牌的生成算法。例如,使用更复杂的加密算法生成令牌。在 Rails 中,可以通过覆盖 ActionController::RequestForgeryProtection 模块中的 generate_csrf_token 方法来实现。首先,在 application_controller.rb 中引入模块:
class ApplicationController < ActionController::Base
  include ActionController::RequestForgeryProtection
  def generate_csrf_token
    # 自定义令牌生成逻辑,比如使用更复杂的加密函数
    SecureRandom.urlsafe_base64(32)
  end
end
  1. 令牌存储位置
    • 操作:默认情况下,CSRF 令牌存储在 session 中。如果要自定义,可以考虑将令牌存储在其他地方,如数据库或 Redis 中。以存储在 Redis 为例,首先需要安装 Redis 相关 gem(如 redis)。然后在 application_controller.rb 中修改 validate_authenticity_token 方法来从 Redis 中获取和验证令牌:
require 'redis'
redis = Redis.new

class ApplicationController < ActionController::Base
  include ActionController::RequestForgeryProtection
  def validate_authenticity_token
    session_token = session[:_csrf_token]
    redis_token = redis.get("#{session_id}:csrf_token")
    if session_token && session_token == redis_token
      super
    else
      raise ActionController::InvalidAuthenticityToken
    end
  end
end
  1. 请求验证时机
    • 操作:默认在请求到达控制器时验证 CSRF 令牌。可以自定义验证时机,比如在 Rack 中间件级别进行验证。创建一个自定义的 Rack 中间件,例如在 lib/csrf_middleware.rb 中:
class CsrfMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    request = ActionDispatch::Request.new(env)
    if request.post? || request.put? || request.delete?
      # 手动验证 CSRF 令牌逻辑
      session = request.session
      token = request.headers['X-CSRF-Token'] || request.params['authenticity_token']
      if token && token == session[:_csrf_token]
        @app.call(env)
      else
        [403, {'Content-Type' => 'text/plain'}, ['Forbidden - CSRF token mismatch']]
      end
    else
      @app.call(env)
    end
  end
end

然后在 config/application.rb 中注册这个中间件:

class Application < Rails::Application
  config.middleware.insert_before ActionDispatch::Callbacks, CsrfMiddleware
end