0

As part of my research for an upcoming project, I am working on a testbed application which has performs anti-XCRF validation.

While starting my research, I found this article, detailing how to do very nearly exactly what I am trying to do.

However, I hit a snag. I implemented the following bits of code, but every time I run my test action, a delete that has the [AntiForgeryValidate] attribute, I keep getting a HttpAntiForgeryException; what's more, in the request headers, there is no __RequestVerificationToken, even though as you can see from my code, I'm taking steps specifically to add it.

Request Verification Token Directive:

app.directive('requestVerificationToken', [
    '$http',
    function ($http) {
        return function (scope, element, attrs) {
            $http.defaults.headers.common['__RequestVerificationToken'] = attrs.requestVerificationToken || "no request verification token";
        };
    }
]);

AntiForgeryExtension.cs:

public static class AntiForgeryExtension
{
    public static string RequestVerificationToken(this HtmlHelper helper)
    {
        // This name is dictated by the name of our validation token directive.
        // See App/Common/requestVerificationTokenDir.js.
        return String.Format("request-verification-token={0}", GetTokenHeaderValue());
    }

    private static string GetTokenHeaderValue()
    {
        string cookieToken;
        string formToken;

        System.Web.Helpers.AntiForgery.GetTokens(null, out cookieToken, out formToken);
        return cookieToken + ":" + formToken;
    }
}

AntiForgeryValidate.cs:

public class AntiForgeryValidate : ActionFilterAttribute
{
    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        string cookieToken = "";
        string formToken = "";

        IEnumerable<string> tokenHeaders;
        if (actionContext.Request.Headers.TryGetValues("__RequestVerificationToken", out tokenHeaders))
        {
            string[] tokens = tokenHeaders.First().Split(':');
            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
        }
        System.Web.Helpers.AntiForgery.Validate(cookieToken, formToken);

        base.OnActionExecuting(actionContext);
    }
}

TestController.cs:

public class HomeController : ApiController
{
    //api/home/DeleteThingy
    [HttpGet]
    [AntiForgeryValidate]
    public HttpResponseMessage DeleteThingy(int thingyId)
    {
        // ...Magic!
        return Request.CreateResponse(HttpStatusCode.OK);
    }
}

...and finally, Index.cshtml:

<div class='container'>
    <input type='hidden' @Html.RequestVerificationToken() />
    <div data-ng-view></div>
</div>

Question: What am I doing wrong, that is causing the __RequestVerificationToken to not appear in the headers that are passed back to the server when I hit a delete button on my front end?

4
  • Have you tested with a tool like Fiddler to ensure your filter attribute is picking up the correct data? Commented Sep 26, 2014 at 16:20
  • How would I even do that? Commented Sep 26, 2014 at 16:22
  • 1
    I mean, crafting an HTTP message to call your API. Fiddler can compose messages like that, very handy! Commented Sep 26, 2014 at 16:22
  • It can also act as a proxy server and intercept all requests to allow you to examine them. Commented Sep 26, 2014 at 16:23

2 Answers 2

1

You probably don't want a directive to add the CSRF token, you want an HTTP interceptor. For example,

app.factory('httpInterceptor', function () {
    return {
        request: function (config) {
            // csrf token for non get calls
            if (config.method != 'GET') {
                config.headers.__RequestVerificationToken =
                    jQuery('input[name="__RequestVerificationToken"]').val();
            }
            return config;
        },
    };
})

This ensures all non HTTP GETs have a CSRF token which is what you want from a security perspective. To add the interceptor, you simply push into an array like so:

app.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push('httpInterceptor');
}])
Sign up to request clarification or add additional context in comments.

3 Comments

How would I go about setting this interceptor up to ensure that it's catching requests as necessary?
As an additional question, in what secure way would we get the antiforgery token from the server to the client?
@cmotley is assuming I think that you already have it in a form element with the name of __RequestVerificationToken. If you do not already have it as a form element, you will either have to add it assuming the server creating the form can generate a valid token or otherwise have another call to the server to get the token directly via an endpoint. Either approach could be considered secure although delivering it via api is probably a tiny bit easier to automate and attack against although clearly having to parse a form request is pretty negligible.
1

The short answer: there was nothing wrong with my code, except I didn't include all of the JavaScript references in my bundling! It was an ID-10T error the whole time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.