2011-09-29 64 views
40

早在2011年2月,Rails就被更改为require the CSRF token for all non-GET请求,即使是API端点的请求也是如此。我理解为什么这是浏览器请求的重要变化的解释,但该博客文章没有提供关于API如何处理更改的任何建议。未禁用CSRF保护的Rails API设计

我对禁用某些操作的CSRF保护不感兴趣。

API应该如何处理这种变化?是否期望API客户端向API发出GET请求以获取CSRF令牌,然后在该会话期间的每个请求中包含该令牌?

看起来,令牌不会从一个POST更改为另一个。假设令牌在会话期间不会改变是否安全?

当会话过期时,我不喜欢额外的错误处理,但我认为它比在每个POST/PUT/DELETE请求之前获取令牌要好。

回答

4

Rails使用“默认安全”约定。跨网站或跨会话请求伪造要求用户拥有浏览器和另一个受信任的网站。这与API无关,因为它们不会在浏览器中运行,也不会维护任何会话。因此,您应该禁用API的CSRF。

当然,您需要通过要求HTTP身份验证或自定义API标记或OAuth解决方案来保护您的API。

+2

我链接的页面不同意:“某些浏览器插件和HTTP重定向的组合可以用来欺骗用户的浏览器进行跨域请求,其中包括攻击者指定的任意HTTP标头,攻击者可以利用它来欺骗ajax和API请求,并绕过内置的CSRF保护并成功攻击应用程序。“ –

+9

“这与API无关,因为它们不在浏览器中运行”:有时,浏览器*是API的客户端 - 即, – antinome

+1

http://stackoverflow.com/questions/9362910/rails -warning -cant-verify-csrf-token-authenticity-for-json-devise-requests/10049965#10049965 – montrealmike

41

老问题,但安全性很重要,我觉得它应该得到一个完整的答案。正如在question中讨论的那样,即使使用API​​,CSRF仍然存在一些风险。是的,浏览器在默认情况下应该防范这种情况,但由于您没有完全控制用户安装的浏览器和插件,所以在您的API中仍应被视为防止CSRF的最佳做法。

我有时看到它的做法有时是从HTML页面本身解析CSRF元标记。我不太喜欢这一点,因为它不适合很多单页+ API应用程序的工作方式,我认为CSRF令牌应该在每个请求中发送,而不管它是HTML,JSON还是XML。

所以我建议通过一个后置过滤器为所有请求传递CSRF令牌作为cookie或标头值。该API可以简单地重新提交,作为Rails已经检查的标题值X-CSRF-Token

这是我如何与AngularJS做到了:

# In my ApplicationController 
    after_filter :set_csrf_cookie 

    def set_csrf_cookie 
    if protect_against_forgery? 
     cookies['XSRF-TOKEN'] = form_authenticity_token 
    end 
    end 

AngularJS automatically looks for a cookie命名XSRF-TOKEN但随时将其命名为任何你想要你的目的。然后,当您提交POST/PUT/DELETE时,您应该设置Rails自动查找的标题属性X-CSRF-Token

不幸的是,AngualrJS已经以X-XSRF-TOKEN的标题值发回了XSRF-TOKEN cookie。人们很容易忽略Rails的默认行为ApplicationController这样,以适应这一点:

protected 

    def verified_request? 
    super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] 
    end 

对于Rails的4.2有一个内置的助手,现在用于验证CSRF应使用。

protected 

    def verified_request? 
    super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN']) 
    end 

我希望这很有帮助。

编辑:在我提交的discussion on this for a Rails pull-request中,传出CSRF令牌通过API登录是一种特别糟糕的做法(例如,某人可以为您的站点创建使用用户凭证而不是令牌的第三方登录名) 。所以c em不驯。这取决于你决定你的应用程序对你的担心。在这种情况下,您仍然可以使用上述方法,但只能将CSRF Coo​​kie发回给已具有已验证会话的浏览器,而不是针对每个请求。这将阻止提交有效的登录,而不使用CSRF元标记。

+3

你的编辑至关重要:服务器应该只为已认证的用户设置CSRF cookie。 – brookr

+0

那么你认为auth的建议是什么?只需简单的基于表单的身份验证,将CSRF标记像平时一样设置在HTML中,然后使用基于标头或基于CSRF的CSR后的API? –

+0

设置不带'httponly'的cookie看起来并不安全。 'httponly'不会阻止JS访问CSRF cookie?相反,如果您使用[fetch's'credentials:'same-origin''参数](例如https://github.com/github/fetch#sending-cookies),它们会与请求一起传递。 为什么使用cookie的非常规名称?如果使用'X-CSRF-Token',ActionDispatch会自动检查并且不会覆盖verified_request?需要。 我建议:'饼干[“X-CSRF令牌”] = {值:form_authenticity_token,仅Http:真正}' –