質問

The MVC4 SPA template has a ValidateHttpAntiForgeryTokenAttribute class with a ValidateRequestHeader function that parses out 2 halves of a RequestVerificationToken header constructed by the client when it assembles the data for an AJAX call. The client AJAX code gets its value from a field on the form that combines a form token and a cookie token. I feel like combining these into a single value loses sight of the purpose of an AntiForgery token; they were separate for a reason. Do we gain any security by using anti-forgery tokens in this way?

Server .vbhtml code (Razor-based):

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

Client AJAX code:

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);
}

Server validation code:

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

What prevents a client from picking any arbitrary pair of cookieToken and formToken that was used in the past, and submitting them together in an AJAX call to get it to go through? Isn't that what anti-forgery functions are supposed to prevent? Is this just a lot of stupid overhead that doesn't improve security, or is there a piece of it that I'm missing?

役に立ちましたか?

解決

What prevents a client from picking any arbitrary pair of cookieToken and formToken that was used in the past, and submitting them together in an AJAX call to get it to go through? Isn't that what anti-forgery functions are supposed to prevent? Is this just a lot of stupid overhead that doesn't improve security, or is there a piece of it that I'm missing?

The anti-forgery token is not designed to prevent a Replay Attack. This is where old values are reused to create another request where the aim is to fool the target machine into accepting a valid instruction from the past.

The anti-forgery token is designed to prevent Cross Site Request Forgery attacks.

A simple example is as follows:

  • You're logged into bank.com on one tab.
  • You get an email to view a funny video by clicking on a link that redirects you to evil.com
  • The web page on evil.com contains a hidden form that is submited by JavaScript to bank.com/make_money_transfer

As you are logged into bank.com and your cookies are sent by the browser, bank.com thinks that you made the request and initiates the money transfer without your knowledge, because from the server's point of view, all is well.

The token is designed to prevent this by having something included in the request payload that cannot be auto submitted by a domain that is not the current domain. Due to the Same Origin Policy another domain cannot access the token value and therefore cannot send a legitimate request via a hidden form, or by any other means. The token is unique per log in session so the attacker could not get a valid combination of token and cookie which can be sent to the server.

Looking at the source code for TokenValidator.cs (albeit C# instead of VB.NET) we can see that the ValidateTokens method checks that the username encoded in the token matches the one of the current HTTP request:

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

This is what will stop an attacker grabbing an old version of the form field value and submitting that in their CSRF attack - their encoded username will not match the logged in user of their victim.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top