My Rails应用程序具有面向公众的表单,可通过AJAX将用户输入作为字符串化JSON传递给控制器。该表单设计用于脱机使用,因此每次访问表单页面而不是第一次访问都是从浏览器缓存(使用缓存清单)提供的。我遇到一个问题,表单提交会返回422 unprocessable entity
错误,除非浏览器历史记录在导航到表单页面之前已被清除...也就是说用户只能提交一个表单,所有后续提交是422
除非他们清除历史记录并返回表单刷新缓存。不幸的是,那不会飞。
我对Rails安全性没有太大的经验,但我的印象是这与CSRF保护有关,而且对于除了第一个之外的任何对表单页面的访问,都会传递过时的CSRF令牌
我的AJAX请求如下所示:
$.ajax({
url: "post/submission",
type: "POST",
dataType: "json",
beforeSend: function(xhr) {xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"))},
data: {"post" : postParameter},
success: function(response){
window.location = '/post/approval';
}
});
目前,布局页面包含<%= csrf_meta_tags %>
,我在应用程序控制器中有标准protect_from_forgery with: :exception
。
关于此表单需要注意的最后一个结构元素是,虽然表单本身是面向公众的,但在单击提交按钮后需要用户登录 - 因此提交将不会成功没有有效的登录信息。
有没有一种安全的方法可以解决这个问题?我确信不言而喻,但我不能让我的用户在每次提交后清除他们的历史记录并重新缓存表单。
答案 0 :(得分:1)
为了在从JavaScript提交POST请求时保持CSRF保护,您需要在表单的有效负载或请求标头中提供当前的身份验证令牌。
从浏览器缓存加载页面时,其身份验证令牌可能已失效,因此需要从后端刷新。一种方法是检测"page loaded from browser cache"
事件并运行ajax请求以获取应该在后端由form_authenticity_token
方法生成的新令牌。
然后,可以将收到的身份验证令牌用于后续的JavaScript POST请求。
例如,描述了检测从高速缓存加载的页面的技术。 in this StackOverflow answer