Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the raw values (not html) from AntiForgeryToken()

This beautiful abstraction lets you place @Html.AntiForgeryToken() in cshtml file which is magically expanded to something like;

<input name="__RequestVerificationToken" type="hidden" value="JjMHm5KJQ/qJsyC4sgifQWWX/WmADmNvEgHZXXuB07bWoL84DrmQzE6k9irVyFSJ5VSYqeUIXgl4Dw4NHSotLwflGYTyECzLvrgzbtonxJ9m3GVPgUV7Z6s2Ih/klUB78GN7Fl4Gj7kxg62MEoGcZw175eVwTmkKJ0XrtEfD5KCVvYIMHNY8MT2l+qhltsGL87c9dII42AVoUUQ2gTvfPg==" />

By mvc before the page is served. However my page has some JavaScript making ajax calls which don't include the token even though it's been added to the form. They are currently getting the expected [HttpAntiForgeryException]: A required anti-forgery token was not supplied or was invalid. because they don't have the token. I'm aware I could parse the value out of the DOM but I shouldn't have to. Are there other ways of accessing/getting this value? To be clear, I mean I'd like an overload of the method that returns just the value as a string or some kind of object that has the name and value both.

To provide a bit more context my form and the relevant JS looks a little like this;

<form action="/settings" method="post"><input name="__RequestVerificationToken" type="hidden" value="JjMHm5KJQ/qJsyC4sgifQWWX/WmADmNvEgHZXXuB07bWoL84DrmQzE6k9irVyFSJ5VSYqeUIXgl4Dw4NHSotLwflGYTyECzLvrgzbtonxJ9m3GVPgUV7Z6s2Ih/klUB78GN7Fl4Gj7kxg62MEoGcZw175eVwTmkKJ0XrtEfD5KCVvYIMHNY8MT2l+qhltsGL87c9dII42AVoUUQ2gTvfPg==" />    <fieldset>
        <h3>User Settings</h3>
        <ul>
            <li>
            label for="password">Password</label>
                <a href="#" id="change_password" class="changePasswordButton">Edit</a>
                <div id="password_section" class="inlineedit">
                    <div>
                        <span for="existing_password">Current password</span> <input autocomplete="off" class="required" id="existing_password" name="existing_password" type="password" />
                    </div>
                    <div>
                        <span for="new_password">New password</span> <input autocomplete="off" class="required" id="new_password" name="new_password" type="password" />
                        <span id="password_strength" />
                    </div>
                    <div>
                        <span for="confirm_password">Confirm password</span> <input autocomplete="off" class="required" id="confirm_password" name="confirm_password" type="password" />
                    </div>
                    <div class="inlinesave">
                        <input type="button" value="Change" onclick="onPostChangePassword();"/>
                        <a href="#" id="cancel_password" class="cancel">Cancel</a>
                    </div>
                </div>
            </li>
    // a bunch more of these that call their own onPostChangeSetting method

onPostChangePassword() does some input validation then;

 if(validPWD && validNewPWD && validConfirmPWD && current_pwd != new_pwd){
                        // Post the password change
                        var currentAjaxRequest = $.ajax({
                            type: "POST",
                            url: "/settings/preferences/changepassword",
                            cache: false,
                            data: {password: $('#new_password').val(), current: $('#existing_password').val(),confirm: $('#confirm_password').val()},
                            success: password_success,
                            error: password_error,
                            dataType: "json"
                        });
                        return true;
                  }

Which ideally (since this is verbatim in a cshtml file) would be modified with something like this;

data: {password: $('#new_password').val(), current: $('#existing_password').val(),confirm: $('#confirm_password').val(),
__RequestVerificationToken:@Html.AntiForgeryValue() }

tl;dr is there a way to interact with the AntiForgeyToken before it's turned into an string of html?

like image 232
evanmcdonnal Avatar asked Jul 08 '15 20:07

evanmcdonnal


People also ask

What does HTML AntiForgeryToken () do?

AntiForgeryToken()Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.

What is __ Requestverificationtoken?

TYPE. __RequestVerificationToken. www.grpgroup.co.uk. This is an anti-forgery cookie set by web applications built using ASP.NET MVC technologies. It is designed to stop unauthorised posting of content to a website, known as Cross-Site Request Forgery.

How do you fix Anti-forgery cookies?

Try quick fixes The common “possible solutions” to anti-forgery token/cookie related issues are disabling output caching and enabling heuristic checks.


1 Answers

You can use code like this (in for example _Layout.cshtml) to add the AntiForgery header to all Ajax POST requests, or you could adapt it for a specific request. (Code assumes you are using jQuery)

@functions{
    private static string TokenHeaderValue()
    {
        string cookieToken, formToken;
        AntiForgery.GetTokens(null, out cookieToken, out formToken);
        return cookieToken + ":" + formToken;
    }
}

<script type="text/javascript">
    $(document).ajaxSend(function (event, jqxhr, settings) {
        if (settings.type == "POST") {   
            jqxhr.setRequestHeader('@ValidateHttpAntiForgeryTokenAttribute.RequestVerificationTokenName',
                                   '@TokenHeaderValue()');
        }
    });
</script>

On the server side for these Ajax calls, you then want to call the overload of AntiForgery.Validate that takes the cookie and form token, which you would enable by adding this attribute to the action methods called via Ajax (explicitly, or by parent controller, or via a filter)

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
                AllowMultiple = false, Inherited = true)]
public sealed class ValidateHttpAntiForgeryTokenAttribute 
                                 : FilterAttribute, IAuthorizationFilter

{
    public const string RequestVerificationTokenName = "RequestVerificationToken";

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            ValidateRequestHeader(filterContext.HttpContext.Request);
        }
        else
        {
            AntiForgery.Validate();
        }
    }

    private static void ValidateRequestHeader(HttpRequestBase request)
    {
        string cookieToken = string.Empty;
        string formToken = string.Empty;

        var tokenValue = request.Headers[RequestVerificationTokenName];

        if (string.IsNullOrEmpty(tokenValue) == false)
        {
            string[] tokens = tokenValue.Split(':');

            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
        }

        AntiForgery.Validate(cookieToken, formToken);
    }
like image 149
stuartd Avatar answered Sep 24 '22 08:09

stuartd