我在一个遗留的rails应用程序中工作,我正在尝试清理一些CSRF漏洞。如果我从表格中删除隐藏的CSRF字段,我仍然可以成功提交表格。
出现问题的唯一指示是日志中的警告:WARNING: Can't verify CSRF token authenticity
。
在某些页面中,protect_from_forgery
会捕获请求,如果没有csrf令牌,但它的命中率取决于页面,则应用程序会崩溃:例如,登录页面在没有令牌的情况下可以工作,但更新用户页面则不能。
我尝试为protect_from_forgery
制定一个自定义策略(Marc Gauthier建议(,类似于:
protect_from_forgery with: :MyStrategy
class MyStrategy
byebug
def initialize(controller)
@contriller = controller
end
def handle_unverified_request
puts "HELLO!"
Rails.logger.warn [
"handle_unverified_request",
"#{@controller.controller_name}-#{@controller.action_name}"
].join(" - ")
end
end
在启动应用程序时,这似乎没有任何作用——byebug
调用将暂停,但我从未收到puts
消息或错误日志。
我也尝试过with: :exception
等常规策略,但没有任何变化,有些页面有效,有些页面无效,但它们是一致的。
通常保护登录用户会话不受CSRF的影响是很重要的,因此在许多应用程序中,通常会在登录/注销时禁用保护,以防止合法用户出错。在大多数情况下,如果会话无论如何都要终止/重置,RF不会造成太大伤害。
查找skip_before_action :verify_authenticity_token
禁用protect_from_forgery
正在安装的操作
检查您的测试方法-隐藏的表单字段并不总是必需的,因为rails也使用ajax表单的X-CSRF-Token
头。要正确测试伪造保护,请进行实际的伪造尝试,例如使用curl
。
检查config.action_controller.allow_forgery_protection
是否未被禁用用于开发/生产,并且控制器或其祖先没有使allow_forgery_protection
过载并为有问题的请求返回false
。不太可能,但应用程序可能忽略了伪造保护的其他部分,请参阅request_forgery_protection.rb
PS。类上下文中的byebug
对这种情况不是很有用,更明显的方法是在handle_unverified_request
中使用raise "Hello CSRF"
Rails似乎在做它应该做的事情,只是重置会话,这在登录表单或忘记密码表单上并不重要。因为我们只有protect_from_forgery
(with: :exception
直到Rails 5中的某个时候才成为默认值(。
人们可能会认为添加with: :exception
会起作用,但事实并非如此。解决方法是提取Exception类的内容并将其放入application_controller
:
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken
end
这适用于登录和忘记密码表单,其他表单(正确地不允许错误请求通过(保持不变,这有点奇怪,因为我认为InvalidAuthenticityToken raised
会同时失败。
当然,这就留下了一个问题,为什么protect_from_forgery with: :exception
不起作用?