我在HTTP请求标头和POST隐藏字段中发送CSRF令牌,以防止CSRF攻击,如下所示。
var request;
var timeout;
function insert(callback)
{
if(!request)
{
CKEDITOR.instances.txtAboutProducts.updateElement();
var contents=$("#txtAboutProducts").val();
var csrf_token=$("#token").val();
if(contents==null||contents=='')
{
alert("Please enter the contents.");
return;
}
request = $.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
datatype:"json",
type: "POST",
url: "../admin_side/AboutProducts.htm",
data: JSON.stringify({"contents":contents, "token":csrf_token}),
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-Token', csrf_token);
},
success: function(response)
{
callback(response);
},
complete: function()
{
timeout = request = null;
},
error: function(request, status, error)
{
if(status!=="timeout"&&status!=="abort") // or just if(status==="error")
{
alert(status+" : "+error);
}
callback(request);
}
});
timeout = setTimeout(function() {
if(request)
{
request.abort();
alert("The request has been timed out.");
}
}, 300000); //5 minutes.
}
}
此函数用于插入CKEditor所持有的CMS内容,以便通过JSON(通过单击按钮时)对Spring进行POST AJAX调用来插入数据库。
函数体开头的条件检查if(!request){...}
只是为了防止重复的AJAX调用(可能是由不耐烦的用户)。
一旦页面加载并在JavaScript变量中检索,随机生成的标记值就会存储在隐藏字段中。
var csrf_token=$("#token").val();
然后,以下处理程序设置一个标题,该标题将与请求一起发送。
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-Token', csrf_token);
},
为什么需要标头?作为隐藏字段发送的令牌是不是足够单独?是否有必要检查标题,因为旧的Flash播放器(仅作为示例)?
使用这种方法,在服务器端,我正在检查,如果标头和隐藏字段的值都匹配(同时检查会话中是否存在该标记{检查标记中是否存在标记会话没有提到虽然})。
上面提到的JavaScript函数调用的Spring控制器类中的方法如下。
@RequestMapping(value=("admin_side/AboutProducts"), method=RequestMethod.POST)
private @ResponseBody CKEditorContentsHandler insert(@RequestBody final CKEditorContentsHandler object, final HttpServletResponse response, final HttpServletRequest request)
{
if(object!=null&&StringUtils.isNotBlank(object.getToken())&&StringUtils.isNotBlank(request.getHeader("X-CSRF-Token"))&&sessionTokenService.isTokenValid(object.getToken())&&object.getToken().equals(request.getHeader("X-CSRF-Token")))
{
aboutProductsService.insert(object.getContents());
object.setMessage("Insertion done successfully.");
object.setStatus(1);
}
else
{
object.setMessage("The authentication token cannot be verified.");
object.setStatus(-1);
}
return object;
}
在CKEditorContentsHandler
,这个方法的第一个参数是一个简单的Java类,它只包含几个属性来满足需要。
还建议将令牌存储在cookie中,并将其与发布的数据(隐藏字段)一起发送,并检查发布的数据和cookie值是否匹配。如果他们不这样做,则可能是CSRF哎呀(因为相同的原始政策,攻击者无法在受害者的浏览器上读取或修改cookie。)
之后,为什么需要标题?实际上,什么是防范(或至少减轻)CSRF攻击的推荐方法?
以下答案总结得很清楚。