在AJAX调用中AntiForgery.Validate的功效和目的

时间:2013-12-11 16:55:21

标签: ajax vb.net security asp.net-mvc-4 antiforgerytoken

MVC4 SPA模板有一个ValidateHttpAntiForgeryTokenAttribute类,其ValidateRequestHeader函数解析客户端在组装AJAX调用数据时构造的RequestVerificationToken标头的两半。客户端AJAX代码从表单上的字段中获取其值,该字段组合了表单令牌和cookie令牌。我觉得将这些组合成一个单一的价值会忽视AntiForgery令牌的目的;出于某种原因,他们是分开的。通过这种方式使用防伪代币,我们是否获得了任何安全保障?

服务器.vbhtml代码(基于Razor):

Public Function GetAntiForgeryToken() As String
    Dim cookieToken As String = String.Empty
    Dim formToken As String = String.Empty

    AntiForgery.GetTokens(Nothing, cookieToken, formToken)
    Return cookieToken & ":" & formToken
End Function

...

@If User.Identity.IsAuthenticated Then
    @<input id="antiForgeryToken" type="hidden" value="@GetAntiForgeryToken()" />
End If

客户端AJAX代码:

function ajaxRequest(type, url, data, dataType) { // Ajax helper
    var options = {
        dataType: dataType || "json",
        contentType: "application/json",
        cache: false,
        type: type,
        data: data ? ko.toJSON(data) : null
    };
    var antiForgeryToken = $("#antiForgeryToken").val();
    if (antiForgeryToken) {
        options.headers = {
            'RequestVerificationToken': antiForgeryToken
        }
    }
    return $.ajax(url, options);
}

服务器验证码:

Private Sub ValidateRequestHeader(request As HttpRequestMessage)
    Dim cookieToken As String = String.Empty
    Dim formToken As String = String.Empty

    Dim tokenHeaders As IEnumerable(Of String) = Nothing
    If request.Headers.TryGetValues("RequestVerificationToken", tokenHeaders) Then
        Dim tokenValue As String = tokenHeaders.FirstOrDefault()
        If Not String.IsNullOrEmpty(tokenValue) Then
            Dim tokens As String() = tokenValue.Split(":"c)
            If tokens.Length = 2 Then
                cookieToken = tokens(0).Trim()
                formToken = tokens(1).Trim()
            End If
        End If
    End If

    AntiForgery.Validate(cookieToken, formToken)
End Sub

什么阻止客户端选择过去使用的任意一对cookieToken和formToken,并在AJAX调用中将它们一起提交以使其通过?这不是防伪功能应该防止的吗?这只是很多愚蠢的开销,不能提高安全性,还是有一部分我缺少?

1 个答案:

答案 0 :(得分:1)

  

什么阻止客户端选择过去使用的任意一对cookieToken和formToken,并在AJAX调用中将它们一起提交以使其通过?这不是防伪功能应该防止的吗?这只是很多愚蠢的开销,不能提高安全性,还是有一部分我缺少?

防伪令牌并非旨在阻止Replay Attack。这是重新使用旧值来创建另一个请求的地方,其目的是欺骗目标机器接受过去的有效指令。

防伪令牌旨在防止Cross Site Request Forgery攻击。

一个简单的例子如下:

  • 您已在一个标签页上登录bank.com
  • 您会收到一封电子邮件,通过点击重定向到您的链接来查看有趣的视频 evil.com
  • evil.com上的网页包含一个隐藏的表单,由JavaScript提交给bank.com/make_money_transfer

当您登录bank.com并且您的Cookie由浏览器发送时,bank.com认为您提出请求并在您不知情的情况下启动汇款,因为从服务器的角度来看,一切都很好。

令牌旨在通过在请求有效内容中包含某些内容无法由当前域的域自动提交来阻止此操作。由于Same Origin Policy另一个域无法访问令牌值,因此无法通过隐藏表单或任何其他方式发送合法请求。每个会话中的令牌都是唯一的,因此攻击者无法获得可以发送到服务器的令牌和cookie的有效组合。

查看TokenValidator.cs的源代码(尽管是C#而不是VB.NET),我们可以看到ValidateTokens方法检查令牌中编码的用户名是否与当前HTTP请求中的用户名匹配:

if (!String.Equals(fieldToken.Username, currentUsername, (useCaseSensitiveUsernameComparison) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
{
     throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, currentUsername);
}

这将阻止攻击者抓取旧版本的表单字段值并在其CSRF攻击中提交 - 他们的编码用户名将与其受害者的登录用户不匹配。