Anti-Forgery Request Recipes For ASP.NET MVC And AJAX

This post discusses solutions for anti-forgery request scenarios in ASP.NET MVC and AJAX:

  • How to enable validation on controller, instead of on each action;
  • How to specify non-constant token salt in runtime;
  • How to work with the server side validation in AJAX scenarios.

Background (Normal scenario of form submitting)

To secure websites from cross-site request forgery (CSRF, or XSRF) attack, ASP.NET MVC provides an excellent mechanism:

  • The server prints tokens to cookie and inside the form;
  • When the form is submitted to server, token in cookie and token inside the form are sent in the HTTP request;
  • Server validates the tokens.

To print tokens to browser, just invoke HtmlHelper.AntiForgeryToken():

<% using (Html.BeginForm())
   { %>
    <%: this.Html.AntiForgeryToken(Constants.AntiForgeryTokenSalt)%>

    <%-- Other fields. --%>

    <input type="submit" value="Submit" />
<% } %>

This invocation generates a token and writes it inside the form:

<form action="..." method="post">
    <input name="__RequestVerificationToken" type="hidden" value="J56khgCvbE3bVcsCSZkNVuH9Cclm9SSIT/ywruFsXEgmV8CL2eW5C/gGsQUf/YuP" />
 
    <!-- Other fields. -->
 
    <input type="submit" value="Submit" />
</form>

and also writes it into the cookie:

__RequestVerificationToken_Lw__=
J56khgCvbE3bVcsCSZkNVuH9Cclm9SSIT/ywruFsXEgmV8CL2eW5C/gGsQUf/YuP

When the above form is submitted, they are both sent to server.

In the server side, [ValidateAntiForgeryToken] attribute is used to specify the controllers or actions to validate them:

[HttpPost]
[ValidateAntiForgeryToken(Salt = Constants.AntiForgeryTokenSalt)]
public ActionResult Action(/* ... */)
{
    // ...
}

This is very productive for form scenarios. But recently, when resolving security vulnerabilities for Web products, problems are encountered.

Turn on validation on controller (not on each action)

The server side problem is, one single [ValidateAntiForgeryToken] attribute is expected to declare on controller, but actually a lot of attributes have be to declared on controller's each POST actions. Because POST actions are usually much more then controllers, the work would be a little crazy.

Problem

Usually a controller contains both actions for HTTP GET requests and actions for POST, and, usually validations are expected for only HTTP POST requests. So, if the [ValidateAntiForgeryToken] is declared on the controller, the HTTP GET requests become invalid:

[ValidateAntiForgeryToken(Salt = Constants.AntiForgeryTokenSalt)]
public class ProductController : Controller // One [ValidateAntiForgeryToken] attribute. 
{
    [HttpGet]
    public ActionResult Index() // Index() cannot work.
    {
        // ...
    }

    [HttpPost]
    public ActionResult PostAction1(/* ... */)
    { 
        // ...
    }

    [HttpPost]
    public ActionResult PostAction2(/* ... */)
    {
        // ...
    }

    // Other actions.
}

If browser sends an HTTP GET request by clicking a link: http://Site/Product/Index, validation definitely fails, because no token is provided (by http://Site/Product/Index?__RequestVerificationToken=???, for example).

As a result, many [ValidateAntiForgeryToken] attributes have be distributed to each POST action:

public class ProductController : Controller // Many [ValidateAntiForgeryToken] attributes.
{
    [HttpGet]
    public ActionResult Index() // Works.
    {
        // ...
    }

    [HttpPost]
    [ValidateAntiForgeryToken(Salt = Constants.AntiForgeryTokenSalt)]
    public ActionResult PostAction1(/* ... */)
    { 
        // ...
    }

    [HttpPost]
    [ValidateAntiForgeryToken(Salt = Constants.AntiForgeryTokenSalt)]
    public ActionResult PostAction2(/* ... */)
    {
        // ...
    }

    // Other actions.
}

This would be a little bit crazy, because one Web product can have a lot of POST actions.

Solution

To avoid a large number of [ValidateAntiForgeryToken] attributes (one for each POST action), the following ValidateAntiForgeryTokenWrapperAttribute wrapper class can be helpful, where HTTP verbs can be specified:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
    AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenWrapperAttribute : FilterAttribute, IAuthorizationFilter
{
    private readonly ValidateAntiForgeryTokenAttribute _validator;

    private readonly AcceptVerbsAttribute _verbs;

    public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs)
        : this(verbs, null)
    {
    }

    public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs, string salt)
    {
        this._verbs = new AcceptVerbsAttribute(verbs);
        this._validator = new ValidateAntiForgeryTokenAttribute()
            {
                Salt = salt
            };
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        string httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
        if (this._verbs.Verbs.Contains(httpMethodOverride, StringComparer.OrdinalIgnoreCase))
        {
            this._validator.OnAuthorization(filterContext);
        }
    }
}

Here only HTTP requests of the specified verbs are validated:

[ValidateAntiForgeryTokenWrapper(HttpVerbs.Post, Constants.AntiForgeryTokenSalt)]
public class ProductController : Controller
{
    // GET actions are not affected.
    // Only HTTP POST requests are validated.
}

Now one single attribute on a controller turns on validation for all POST actions in that controller.

It would be nice if HTTP verbs can be specified on the built-in [ValidateAntiForgeryToken] attribute. And, this is very easy to implement.

Specify non-constant salt in runtime

By default, the salt should be a compile time constant, so it can be used for the [ValidateAntiForgeryToken] or [ValidateAntiForgeryTokenWrapper] attribute.

Problem

One Web product might be sold to many clients. If a constant salt is evaluated in compile time, after the product is built and deployed to many clients, they all have the same salt. Of course, clients do not like this. Even some clients might expect a configurable custom salt. In these scenarios, salt is required to be a runtime value.

Solution

In the above [ValidateAntiForgeryToken] and [ValidateAntiForgeryTokenWrapper] attributes, the salt is passed through constructor. So one solution is to remove that parameter:

public class ValidateAntiForgeryTokenWrapperAttribute : FilterAttribute, IAuthorizationFilter
{
    public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs)
    {
        this._verbs = new AcceptVerbsAttribute(verbs);
        this._validator = new ValidateAntiForgeryTokenAttribute()
            {
                Salt = Configurations.AntiForgeryTokenSalt
            };
    }

    // Other members.
}

But this smells bad because the injected dependency becomes a hard dependency. So the other solution to work around the limitation of attributes, is moving validation code into controller:

public abstract class AntiForgeryControllerBase : Controller
{
    private readonly ValidateAntiForgeryTokenAttribute _validator;

    private readonly AcceptVerbsAttribute _verbs;

    protected AntiForgeryControllerBase(HttpVerbs verbs, string salt)
    {
        this._verbs = new AcceptVerbsAttribute(verbs);
        this._validator = new ValidateAntiForgeryTokenAttribute()
            {
                Salt = salt
            };
    }

    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        string httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
        if (this._verbs.Verbs.Contains(httpMethodOverride, StringComparer.OrdinalIgnoreCase))
        {
            this._validator.OnAuthorization(filterContext);
        }
    }
}

Then just make controller classes inheriting from this AntiForgeryControllerBase class. Now the salt is no long required to be a compile time constant.

Submit token via AJAX

For browser side, once server side turns on anti-forgery validation for HTTP POST, all AJAX POST requests will fail by default.

Problem

In AJAX scenarios, the HTTP POST request is not sent by form. Take jQuery as an example:

$.post(url, {
    productName: "Tofu",
    categoryId: 1 // Token is not posted.
}, callback);

This kind of AJAX POST requests will always be invalid, because server side code cannot see the token in the posted data.

Solution

Basically, the tokens must be printed to browser then sent back to server. So first of all, HtmlHelper.AntiForgeryToken() need to be called somewhere. Now the browser has token in both HTML and cookie.

Then jQuery must find the printed token in the HTML, and append token to the data before sending:

$.post(url, {
    productName: "Tofu",
    categoryId: 1,
    __RequestVerificationToken: getToken() // Token is posted.
}, callback);

To be reusable, this can be encapsulated into a tiny jQuery plugin:

/// <reference path="jquery-1.4.2.js" />

(function ($) {
    $.getAntiForgeryToken = function (tokenWindow, appPath) {
        // HtmlHelper.AntiForgeryToken() must be invoked to print the token.
        tokenWindow = tokenWindow && typeof tokenWindow === typeof window ? tokenWindow : window;

        appPath = appPath && typeof appPath === "string" ? "_" + appPath.toString() : "";
        // The name attribute is either __RequestVerificationToken,
        // or __RequestVerificationToken_{appPath}.
        var tokenName = "__RequestVerificationToken" + appPath;

        // Finds the <input type="hidden" name={tokenName} value="..." /> from the specified window.
        // var inputElements = tokenWindow.$("input[type='hidden'][name=' + tokenName + "']");
        var inputElements = tokenWindow.document.getElementsByTagName("input");
        for (var i = 0; i < inputElements.length; i++) {
            var inputElement = inputElements[i];
            if (inputElement.type === "hidden" && inputElement.name === tokenName) {
                return {
                    name: tokenName,
                    value: inputElement.value
                };
            }
        }
    };

    $.appendAntiForgeryToken = function (data, token) {
        // Converts data if not already a string.
        if (data && typeof data !== "string") {
            data = $.param(data);
        }

        // Gets token from current window by default.
        token = token ? token : $.getAntiForgeryToken(); // $.getAntiForgeryToken(window).

        data = data ? data + "&" : "";
        // If token exists, appends {token.name}={token.value} to data.
        return token ? data + encodeURIComponent(token.name) + "=" + encodeURIComponent(token.value) : data;
    };

    // Wraps $.post(url, data, callback, type) for most common scenarios.
    $.postAntiForgery = function (url, data, callback, type) {
        return $.post(url, $.appendAntiForgeryToken(data), callback, type);
    };

    // Wraps $.ajax(settings).
    $.ajaxAntiForgery = function (settings) {
        // Supports more options than $.ajax(): 
        // settings.token, settings.tokenWindow, settings.appPath.
        var token = settings.token ? settings.token : $.getAntiForgeryToken(settings.tokenWindow, settings.appPath);
        settings.data = $.appendAntiForgeryToken(settings.data, token);
        return $.ajax(settings);
    };
})(jQuery);

In most of the scenarios, it is Ok to just replace $.post() invocation with $.postAntiForgery(), and replace $.ajax() with $.ajaxAntiForgery():

$.postAntiForgery(url, {
    productName: "Tofu",
    categoryId: 1
}, callback); // The same usage as $.post(), but token is posted. 

There might be some scenarios of custom token, where $.appendAntiForgeryToken() is useful:

data = $.appendAntiForgeryToken(data, token);
// Token is already in data. No need to invoke $.postAntiForgery().
$.post(url, data, callback);

or $.ajaxAntiForgery() can be used:

$.ajaxAntiForgery({
    type: "POST",
    url: url,
    data: {
        productName: "Tofu",
        categoryId: 1
    },
    success: callback, // The same usage as $.ajax(), supporting more options.
    token: token // Custom token.
});

And there are special scenarios that the token is not in the current window. For example:

  • An HTTP POST request can be sent from an iframe, while the token is in the parent window or top window;
  • An HTTP POST request can be sent from an popup window or a dialog, while the token is in the opener window;

etc. Here, token's container window can be specified for $.getAntiForgeryToken():

data = $.appendAntiForgeryToken(data, $.getAntiForgeryToken(window.parent));
// Token is already in data. No need to invoke $.postAntiForgery().
$.post(url, data, callback);

or $.ajaxAntiForgery() can be used:

$.ajaxAntiForgery({
    type: "POST",
    url: url,
    data: {
        productName: "Tofu",
        categoryId: 1
    },
    success: callback, // The same usage as $.ajax(), supporting more options.
    tokenWindow: window.parent // Token is in another window.
});

If you have better solution, please do tell me.

46 Comments

  • You could always seed the Salt with a static property generate from one or more Guid(s).

    public static class SaltCellar
    {
    private static readonly string s_Salt = Guid.NewGuid().ToString("N") + Guid.NewGuid.ToString("N");

    public static Default
    {
    get
    {
    return s_Salt;
    }
    }
    }

  • Agree. But this SaltCellar.Default cannot be used directly:

    [ValidateAntiForgeryToken(Salt = SaltCellar.Default)]

    Because SaltCellar.Default is not a compile-time constant.

    The second topic "Specify non-constant salt in runtime" is talk about this kind of scenarios.

  • I think static salt does not improve security. Because hacker may, has some script to get current salt (that is used for everybody in application) or click view source for doing the same thing. The best way to solve this problem is unique salt for each request/response. I think Generated salt should depend on user; current time and application build number.

  • Hacker can view source and manipulate the post data using tools like firebug, how do we restrict this ?

  • Hacker definitely can do this on his own computer.

    However, this CSRF attack is supposed to happen on the other people's computer. For how to do CSRF attack, Phil Hacck has an excellent post: http://haacked.com/archive/2009/04/02/anatomy-of-csrf-attack.aspx

  • Wouldn't it be easier to just serialize the form in your jquery post?
    $.post($('#myform').attr('action'), $('#myform').serialize(), function(data){});

    A bit simpler and will also support multiple forms on a single page.

  • I think this is a nice and smart way. It works if on the single page:
    1) all data (which is going to be POSTed) are persisted in the ;
    2) the token is also inside that .

    The above $.postAntiForgery() and $.ajaxAntiForgery() can work for general scenarios. In many pages, not all data for POST are persisted with one element. And the token would not be always inside one form element. There are many ajax pages with no elements at all. And the data might be represented in many ways (like an ). Sometimes token could be even in another window (consider the iframe and dialog scenarios).

    And in most cases, all that need to do is replacing $.post() invocation with $.postAntiForgery(). I hope this could be as simple as your solution :)

  • Nice post. BTW, as a point of clarification. The actual value in the hidden input and the value in the cookie are not exactly the same. One of those values has a salt and username encrypted as part of the value.

    Thus when the form post and cookie are sent to the action, we don't blindly compare the value in the cookie and form post. First, we unencrypt both values and then compare the token value within each payload for equality. This is much safer.

  • Yes. I read this from MVC source code.

    And I think this is why the mechanism can work in such scenario:
    1. In a user session, user opens a page A, gets a input with value "a", and cookie is "a".
    2. User opens a page B (in another IE tab, or in an iframe, etc.), gets a input with value "b", now cookie is "b".
    3. On page A, user send a POST request, with "a" in data, "b" in cookie. Server will figure out this to be valid.

  • Sounds like exactly the issue I was trying to solve when building a PHP and Jquery site, still haven't gotten a resolution and may change to MVC to take advantage of this security functionality.

    J.

    http:www.javerter.com
    The Worlds Fastest Currency Converter, powered by JQuery

  • Great article!
    Thanks!
    Very helpful!

  • Really disapointed that microsoft has gone ahead an ignored the valid xhtml schema but giving this input a name and id that starts with "__". Please Microsoft fix this. It was a problem with viewstate in asp.net webforms

  • I tried "__" with the W3C Markup Validation Service, it says Ok.

    W3C Markup Validation Service:
    http://validator.w3.org/

    Code:




    Test






  • Thanks for the good information...very good blog site.

  • I am using .Net Framework 3.5 SP1 and MVC 1.0, I am not able to find the method GetHttpMethodOverride(), the HttpRequestBase. Is it specific to any version of .Net / MVC.

  • ASP.NET MVC’s anti-CSRF helpers work very nicely, but you should be aware of a few limitations:
    All legitimate visitors must accept cookies (otherwise, [ValidateAntiForgeryToken] will deny their form posts). Arguably this isn’t a limitation, because unless visitors allow cookies, you probably don’t have anything to protect anyway.
    It only works with POST requests, not GET requests. Arguably this isn’t a limitation, because under the normal HTTP conventions, you shouldn’t be using GET requests for anything other than read-only operations.
    It’s easily bypassed if you have any XSS holes on your domain. An XSS hole would allow an attacker to read a victim’s anti-forgery token value, then use it to forge valid posts. So, don’t have XSS holes!
    It relies on the potential victim’s browser implementing cross-domain boundaries solidly. Browsers are supposed to stop foreign domains from reading your app’s response text and cookies, and are supposed to stop foreign domains from writing cookies to your domain. If an attacker manages to find a way around this, they can bypass [ValidateAntiForgeryToken]. Of course that’s not supposed to be possible. For the most part, modern browsers block this line of attack.

  • This topic had been useful for me.

  • There should be an easy way in MVC 3 to include the AntiForgeryToken in Ajax.ActionLink without doing all this work.

    By the way, the post from xinqikan.com on Nov 25 is almost word for word out of Steven Sanderson's book, "Pro ASP.NET MVC 2 Framework" on p. 580.

  • I cannot get the ajax version to work when I set the content type to JSON. I serialize a series of objects (that I accept as a List in my controller) and I have tried putting the token both inside and outside the 'data' object with no success :-(

  • Anti forgery request recipes for asp net mvc and ajax.. Great! :)

  • hi,
    this is a nice post however i looked at the ValidateAntiForgeryToken validation algorithm:

    string text = context.Request.Form[antiForgeryTokenName];
    if (string.IsNullOrEmpty(text))
    {
    throw AntiForgeryWorker.CreateValidationException();
    }

    it is only looking for the token via the Forms collection! thats very bad!

    say, when you post request json via $.ajax, using the json context-type like this:
    $.ajaxSetup({
    type: "POST",
    contentType: "application/json; charset=utf-8",
    dataType: "json"
    });

    it won't work! :(

    your plugin also isn't working then because the send json-data becomes invalid:

    $.ajaxAntiForgery({
    url: that.saveActionUrl,
    data: JSON.stringify(dataToSend),
    success: function (data) {
    ...
    }
    });

    i.e. the broken post request "json"-data might look like this then:

    {"calendarDTOs":[{"id":0,"dateText":"20.07.2011","employeeId":47,"date":"2011-07-19T22:00:00.000Z","type":"uta","approved":null}]}&__RequestVerificationToken=U81Llj6F5UzRDp19kwz4K60WQllLFjD4Bs8Fpe%2BZDoHWwYIheG821zNeT%2F0sFlHxUbrMle1EhKbp5wHf%2FnjaTcefLqbvuJCkGGSJnKSWdBZUR93%2FbNAbLat4SFcT5DtQfNnyoeojlakJFKYhb0lpyfDoEwg4oqF%2BtarGRpbpaDYFPAGJM%2FhJGlIe7xyh1AAz

    hopefully this gets fixed with the next mvc version, so that the filter will accept the token not only via Forms collection!

  • Agree. That cannot work. We should not mix form data and json data.

    Ideally, the data and the token should be merged into either json data:
    { calendarDTOs: "value" , __RequestVerificationToken: "value" }

    or form data:
    calendarDTOs=value&__RequestVerificationToken=value

    However, since the server side does not support extracting token from json data, if you still need to send json data to server, a work around could be like this:
    jsonData={calendarDTOs: "value"}&__RequestVerificationToken=value

    Then the token can be extracte by server. Also you get your json data in request.Form["jsonData"]

  • Anti forgery request recipes for asp net mvc and ajax.. Awesome :)

  • Anti forgery request recipes for asp net mvc and ajax.. OMG! :)

  • @Dixin
    How may i pass this tokens in case of using jQGrid, when it is doing ajax requests?

  • Hello,
    Great post! I'm just wondering about the case of a ajax request without a form, it seems like even if you send the token on the request data like you're doing on your examples it will fail anyway because the validation on the server needs the token to the included on the collection Request.Form and then compare with the cookie.
    Does the ajax request worked for you?

  • Yes it works.

    I'm just wondering about the case of a ajax request without a form

    -> Actually this is quite usual. Many user interactions are implemented without a element.

    it will fail anyway because the validation on the server needs the token to the included on the collection Request.Form and then compare with the cookie.

    -> As long as browser sends data with "POST" http request, server gets the data in Request.Form. Submitting a will send a "POST" reques. And an ajax "POST" request does the same thing. You can use the tool "fiddler" to check the detail of either http request (form / ajax). You will see they are the same thing.

  • @Bohemian,

    I didn't see your entire code but I think this may not be right:
    var model = JSON.stringify(m);
    model = model + '&' + t;

    Can you try this:
    var m = {a: 1, b: 2};
    var token = $.getAntiForgeryToken();
    token.jsonData = JSON.stringify(m);
    var model = $.param(token);

    Now model should be something like __RequestVerificationToken=value&jsonData={a:1, b:2}.

  • @Bohemian,

    I didn't see your entire code but I think this may not be right:
    var model = JSON.stringify(m);
    model = model + '&' + t;

    Can you try this:
    var m = {a: 1, b: 2};
    var token = $.getAntiForgeryToken();
    token.jsonData = JSON.stringify(m);
    var model = $.param(token);

    Now model should be something like __RequestVerificationToken=value&jsonData={a:1, b:2}.

  • I am trying to get your code working in the Solution section. Does it really begin with (function ($) {
    and not jQuery's $(function()?

  • The last line of your code })(jQuery); gives "Expression is not assignmnet or call)

  • The above code is defining methods on jQuery so that you can use later.

    In your HTML, have you referenced to jQUery before this?

  • I have referenced jQuery before this with $(function () {

  • Great post, really concise and helpful, thanks!

  • Hello!
    Q1) Is it important to append the app path in the ajax request token name like:
    tokenName = "__RequestVerificationToken" + appPath;
    In my test app the cookie looks like: __RequestVerificationToken_L0lDSVNEYXNoYm9hcmREZXZEZW1=/VudXrO4PzbGVl...
    and request body looks like "__RequestVerificationToken=yfpf3m1Q0u4YiU"
    Is that wrong? The app seems to work.
    Q2) How important is it to use encodeURIComponent on token name and value?
    Thanks ...great post! :)

  • @TheBigBull
    1. Yes, you got it:). If you take a look at the sample at the beginning of the article, there is always a appPath in the cookie.

    2. encodeURIComponent is important and needed. Otherwise you may get random failure, because the string may has some special character, which may be converted to %xx by the browser. Actually, I got this error and that's why I am using encodeURIComponent :).

  • @Candice

    data: JSON.stringify(searchCriteria) is the problem.

    try this:
    data: $.params(searchCriteria)

    or try my $.appendAntiForgeryToken():
    data: $.appendAntiForgeryToken(searchCriteria, {__RequestVerificationToken: $("#antiforgerytoken").val()})

  • @Cristiano Dias

    I am not sure what happened in your website because I cannot see your project. Please make sure you have this in your HTML:


    and you have this in your cookie (in your ajax POST request's header):
    __RequestVerificationToken_Lw__=
    J56khgCvbE3bVcsCSZkNVuH9Cclm9SSIT/ywruFsXEgmV8CL2eW5C/gGsQUf/YuP

    and you have this in your ajax POST request's body:
    ...&__RequestVerificationToken=J56khgCvbE3bVcsCSZkNVuH9Cclm9SSIT/ywruFsXEgmV8CL2eW5C/gGsQUf/YuP

    You can use fiddler to observe your ajax requests: http://www.fiddler2.com/fiddler2/

  • Thinking Why it is mandatory to set the verb in the constructor of the wrapper class. Cant it be set inside the constructor without user has to specifically set while applying the attribute. So inside the constructor HttpVerbs.Post will be set by default.

  • I have a page with an ajax call and antiforgery token as described on this page. Works great until I navigate to another page, and then click the back button.

    Now the original page resends it's ajax call, but the hidden input token and the cookie's token no longer match: "The anti-forgery cookie token and form field token do not match."

    Any tips?

  • I tried approaches that you have given for @Bohemian and @Candice, both didn't work. Note: I don't have a form POST.

  • I seldom leave comments, however i did a few searching and wound
    up here Anti-Forgery Request Recipes For ASP.NET MVC And AJAX - Dixin's Blog.
    And I do have 2 questions for you if it's allright.
    Is it only me or does it look like some of the remarks appear as if
    they are coming from brain dead people? :-P And, if you
    are posting at other social sites, I'd like to follow everything new you have to post.
    Could you make a list of every one of all
    your shared pages like your Facebook page, twitter feed, or linkedin profile?

  • ECjBIw Thank you for your article post.Much thanks again. Awesome.

  • I don't see why you couldn't bypass this with PHP, which doesn't have a boundary restriction on reading response text.

    1. Setup a shared cookie jar in cUrl
    2. Make the request for the form, pass the token value,
    3. Use it to set a cookie with that same value and add that value to your POST parameters

    Am I missing something?

  • I meant parse the token, not pass. You would also need to pragmatically log the user in, if the form required authentication of course.

    I've built many scrappers in PHP, and in fact this type of problem cannot be remedied this easily. For forms, the best method is to use a reCAPTCHA.

  • salt is now obsolete in mvc4

Add a Comment

As it will appear on the website

Not displayed

Your website