As I read the anti-forgery system that ASP.NET MVC implements generate a token that can be reused across the same session, my question is why then this token changes every time I generate a new form in my app? I am talking about the hidden input field, not about the cookie value.
Thanks.
No. the token is not reused.
Every page refresh will generate a new value in Form input (and Cookie as well, in case it is invalid or not exist). upon submission, the server will try to match the form value against the Cookie value.
Taken from Professional ASP.NET.MVC 3 book
Token Verifi cation ASP.NET MVC includes a nice way of preventing CSRF
attacks, and it works on the principle of verifying that the user who
submitted the data to your site did so willingly. The simplest way to
do this is to embed a hidden input into each form request that
contains a unique value. You can do this with the HTML Helpers by
including this in every form:
<form action=”/account/register”
> method=”post”> <#Html.AntiForgeryToken()> … </form>
Html.AntiForgeryToken will output an encrypted value as a hidden
input: This value
will match another value that is stored as a session cookie in the
user’s browser. When the form is posted, these values will be matched
using an ActionFilter:
[ValidateAntiforgeryToken] public ActionResult
> Register(…)
Related
When I want to submit a form to save an edited record, I should pass its Id to controller. Then a client (or attacker) may change some information (e.g. this Id) on the form that I don't want to be changed. I can create a hashed hidden field to check that read-only fields have not been changed and verify it when it is posted to controller.
Is there any other good practice for this issue?
Thanks
You can encode data you want to protect with server side algorithm, that way that view receives encoded data only. When user passed the form to controller you decode data and check for validity. Also remember to implement not only client side validation, but also server side validation for your model.
AntiForgeryToken: A great feature in ASP.NET MVC is the AntiForgeryToken. This Generates a hidden form field (anti-forgery token) that is validated when the form is submitted. The anti-forgery token can be used to help protect your application against cross-site request forgery
.cshtml Code
#using (Html.BeginForm("Index", "Home"))
{
#Html.AntiForgeryToken()
}
Controller Code
[ValidateAntiForgeryToken()]
[HttpPost]
public ActionResult Index()
{
//Your Code
return View();
}
We've had a penetration test on a website and they're saying we shouldn't be passing readable data in a querystring (it's an Email Address).
The querystring is being created by the application when ModelState.isValid fails and it returns the model to the view with a HTTP GET. We are being told the EmailAddress value should be encrypted (the site is SSL).
The penetration result :
GET /Membership/RegisterMe?__RequestVerificationToken=26Oajd6AG17YkHhyZz8-pArBuKEEer7V0b86f0aR_jHXs2JqYRE8NHvhz1zCcKWtQ6eVtxtdkTvC6HjG1ami2d-2CPn8Ieedwc77fIoMB941&EmailAddress=SomeOnesEmail.com
We tried to convert the value after it's submitted by doing the following in the controller, so if validation fails it will returns an encrypted value in the querystring:-
ModelState.Remove("EmailAddress");
model.EmailAddress = Helpers.Encryption.Encrypt(model.EmailAddress);
But it loses the validation messages on the property, for example if it's an invalid email address.
Turns out using HTML Helpers in your View makes it difficult to change Model values on Postback (validation fail). It will always return the original values from the HTML helpers in the form. You can change it like so:-
ModelState.Remove("EmailAddress");
Model.EmailAddress = //new value;
My problem was I needed to keep the EmailAddress value but encrypt it before Postback, so the value is not exposed in the querystring. The above was no good as using ModelState.Remove, you also lose the Validation message (invalid Email Address etc.).
Reason was because of the HTML Helpers in the form - #Html.TextBoxFor. Changed this to
<input type="text" name="EmailAddress">
You can then change the value in the controller before postback and maintain the validation errors
Model.EmailAddress = Encrypt(Model.EmailAddress);
The postback value is encrypted and the appropriate validation error messages are displayed to the user in the View. Then did some simple code in the View to decrypt the Model.EmailAddress.
I have got the MVC4 [ValidateAntiForgeryToken] attribute working perfectly. However, I don't understand what I am seeing in Fiddler. The cookie sent by the server to the browser is set to this value:
__RequestVerificationToken=FVcmfj07ZEuBdjGuqWu14KIzolxr0ArLgvbNdnq0c4DFywxSA31yIHbm2IzgTPMVhMl4STEh2re8oGmwsSjKtSBTolCsmyGGRnLE1qurUqA1
but the hidden form input is set to this value:
OxjO3NjS1ly-bqP9RnYK9Vx8ZJyLGVCuTQEuSCAQWofVmuJaRkEcnHAHWcDurXaH6DhUiZ6XY5wCgi70u19mPy9sydMrkuS9qlWMXxGL_401
i.e. they appear different where they should match. Am I not understanding cookies properly and perhaps the first string is not the actual 'value' of the cookie encrypted?
Source Pro ASP.NET MVC 3 Framework:
The __RequestVaerificationToken hidden field contains a random component (matching the one in the cookie), but that's not all. If the user is logged in, then the hidden field value will also contain their user name (obtained from HttpContext.User.Identity.Name and then encrypted).
[ValidateAntiForgeryToken] checks that this matches the logged-in user. This adds protection in the unlikely scenario where an attacker can somehow write (but not read) cookies on your domain to a victim's browser and tries to reuse a token generated for a different user.
In a web page we provide a hyperlink (GET) that the User may click on to authenticate:
#Html.ActionLink("Please Login", "MyMethod", "MyController")
This maps to the following controller method which returns a View:
[RequireHttps]
public ActionResult MyMethod()
{
return this.View(new MyModel());
}
This View contains the Form in which the User supplies their credentials; the Form contains the required AntiForgeryToken.
When the User submits the form, the following Controller method is called:
[HttpPost]
[RequireHttps]
[ValidateAntiForgeryToken]
public ActionResult MyMethod(MyModel model)
{
// my logic
}
This works perfectly well, most of the time...
However, if the User leaves their browser open for a "significant" period of time and then performs the following steps in quick succession:
Clicks on the hyperlink (GET) to load the log-in form
Completes the form and submits
They get an exception informing them that the Anti-Forgery token was either not provided or was invalid.
I don't understand why this is the case: the View (containing the form) is created after the browser was dormant and so the anti-forgery tokens should all be "fresh". However, something is evidently wrong with this design, but I'm not sure how best to rectify it.
Thanks in advance if you have any suggestions.
Griff
I'm dealing with this same problem and while I understand the issue, I'm not sure yet of the best resolution.
The Anti-ForgeryToken process places an input value in the form with a second value stored in a cookie RequestVerificationToken. Both of these are submitted to the server and if they don't match the error is thrown.
The RequestVerficationToken cookie has an expiration value set to be Session. So when the user leaves the browser open on the page for a long time and then submits, the cookie's time stamp is compared to the session timeout value on the server — a default of 20 minutes or so — and having been exceeded, it is removed and thus token validation fails.
Possible solutions, all of which have potential issues;
Put a javascript timer on the page and refresh at some value less
than your session timeout.
Catch the System.Web.Mvc.HttpAntiForgeryException on the server — and redirect
to the same page.
Increase your session timeout
Change the expiration on the anti-forgery token
We received a request from our client that they want every form on their site to store certain fields in a cookie, so that every time after the first submit, the form would be filled in with data from the cookie.
We created an actionfilter attribute to use on the methods that required this functionality. The idea would be to store the data of the FormCollection in the cookie, and every time the action gets called, we would check if the cookie exists and update the value accordingly.
The problem, is that the Form is read only, and cannot be modified. How would I achieve this functionality.
One technique would be to create a custom model binder which populates the model from the cookie for the appropriate properties, and then uses the default model binder for the remaining properties.
You could derive from DefaultModelBinder as described here and here.