虽然这是一个老问题,但安全性很重要,我认为它值得完整的回答。正如在question中讨论的那样,即使是通过API,仍然存在CSRF的某些风险。是的,浏览器默认情况下应该防止此类攻击,但由于您无法完全控制用户安装的浏览器和插件,因此在API中防范CSRF仍应被视为最佳实践。
有时候,我看到的做法是从HTML页面本身解析CSRF meta标签。但我并不喜欢这种方法,因为它与当今许多单页面+API应用程序的工作方式不符,我认为无论是HTML、JSON还是XML,都应该在每个请求中发送CSRF令牌。
因此,我建议通过过滤器将CSRF令牌作为cookie或头部值传递给所有请求。API可以简单地将其作为X-CSRF-Token
的头部值重新提交,而Rails已经进行了检查。
以下是我在AngularJS中的实现:
after_filter :set_csrf_cookie
def set_csrf_cookie
if protect_against_forgery?
cookies['XSRF-TOKEN'] = form_authenticity_token
end
end
AngularJS 自动寻找名为 XSRF-TOKEN
的 cookie,但您可以根据您的需要随意更改名称。然后,在提交 POST/PUT/DELETE 请求时,应设置头部属性 X-CSRF-Token
,Rails 会自动查找。
不幸的是,AngualrJS 已经将 XSRF-TOKEN
cookie 发送回一个名为 X-XSRF-TOKEN
的头部值中。您可以轻松地在 ApplicationController
中覆盖 Rails 的默认行为以适应此情况,如下所示:
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
我希望这对你有所帮助。
编辑:在我提交的Rails pull-request的讨论中,发现通过API传递CSRF令牌进行登录是一种特别糟糕的做法(例如,某人可以创建使用用户凭据而不是令牌的第三方登录您的网站)。所以要小心。由你来决定你对应用程序的担忧程度。在这种情况下,您仍然可以使用上述方法,但仅将CSRF cookie发送回已经具有经过身份验证会话的浏览器,而不是每个请求都发送。这将防止在不使用CSRF元标记的情况下提交有效的登录。
httponly
的 cookie 似乎不安全。使用httponly
是否可以防止 JS 访问 CSRF cookie?如果您使用 fetch 的credentials: 'same-origin'
参数,则它们将随请求一起传递。为什么要使用非常规的 cookie 名称呢?如果您使用X-CSRF-Token
,ActionDispatch 将自动检查它,无需覆盖verified_request?
。我建议使用:cookies["X-CSRF-Token"] = { value: form_authenticity_token, httponly: true }
。 - Olivier Lacanhttponly: false
,但在浏览器中令牌始终是httponly
。我无法让我的React前端能够访问这些cookie。 - Aaron Parisi