3

我在 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 Players 是否需要检查标题(仅作为示例)?


使用这种方法,在服务器端,我正在检查标头和隐藏字段的值是否匹配(以及检查会话中该令牌的存在{检查会话中令牌的存在无处虽然提到})。

上述 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 攻击的方法是什么?


以下答案很好地总结了它。

反 CSRF 令牌和 Javascript

4

0 回答 0