ASP.NET MVC - ValidateAntiForgeryToken expiring - asp.net-mvc

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

Related

Redirect to a page along with a parameter on session timeout

I have a page for example: www.samplepage.com/Index?id=1001,
I then store this id in a session to use it throughout the solution. But when the session times out I need to redirect to the sample page (www.samplepage.com/Index?id=1001), I am using the OnActionExecuting ActionFilter method. But now I loose the id value as it is stored in a session. And the id changes based on certain conditions and I cannot hard code it.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext session = HttpContext.Current;
if (HttpContext.Current.Session["EmployerId"] == null)
{
filterContext.Result = new RedirectResult("~/Index");
return;
}
base.OnActionExecuting(filterContext);
}
How can I store id value and use it in the ActionFilter?
The session timeout is a sliding timeout. Each time a request is received by the server from the client, the timeout is reset. The only way the session can expire is if the client has absolute no interaction with the server for more than the specified timeout. As a result, there's no way to redirect when the session expires, because that would first require a request from the client to issue the redirect (request-response: the server cannot respond unless it first receives a request, and a redirect is a type of response).
Furthermore, by the time the user attempts to make a new request after the session has expired, well the session has already expired. The id you're storing, therefore is also gone, so redirect or not, there's no way to reset the id as there's no record of what it actually was.
Personally, I'd recommend simply implementing a piece of long-polling AJAX that will have the effect of keeping the session continuously active as long as the page is open in the user's browser. You just need to hit the server at some point before the session expires, so if that's 20 minutes, send the AJAX request after 19 minutes and you're good for another 20. You could also simply set the timeout longer, such that maybe expiration won't be as much of an issue.
Short of that, you'll need to ensure that the links you generate include the id as part of the query string, and any forms include the id as a hidden field. That way, if the session ever expires, the id still exists somewhere in the request and you can simply set it in the session again.

Force a user to re-enter credentials before submit

Using MVC5, i have an application which a user must be logged into, and then can perform standard actions on some data (create, edit, delete).
I would like to add a credentials prompt, whenever a certain task if performed. So say for example a user is editing a row of data. I want them to be prompted to enter their login credentials again when they hit the Save button, before the row is updated. To be clear, they are ALREADY logged in, i just want to force them to re-confirm their credentials before being allowed to save.
How can i do this in the controller? I want a seperate screen/popup to show, asking for username and password (which will then be checked to ensure correct user credentials) before allowing update of the data.
I looked at creating a new method in the controller, which is passed a username and password, which looks after checking the users credentials again. But how do I go about calling this from the Edit screen, when I also need a popup to appear? Do i go down the route of adding a hidden div on the Edit view, which shows when the user clicks the Save button, and it then calls the method?
Generally, you're expected to attempt a solution, first. Then, if you run into specific issues, you can ask a question about those specific issues. I will tell you that this should be relatively straight-forward. All you need is for the user to re-enter their password. Just add a password input to your edit form and bind it to something on your view model, or you can simply bind it directly to an action parameter, in addition to your view model:
[HttpPost]
public ActionResult MyAction(MyViewModel model, string password)
If you want it to be done in a popup, simply include the popup HTML within the form (so the that the input in the popup will be part of the form) or you'll need to use JavaScript to set another input within the form, which would be bound to either a view model property or action param. Either way, the point is that the password should be posted along with the rest of the form data.
Once inside your post action, you can verify the password by manually:
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var verifyPassword = UserManager.PasswordHasher.VerifyHashedPassword(user.PasswordHash, password);
if (verifyPassword == PasswordVerificationResult.Failed)
{
ModelState.AddModelError("", "Password incorrect.");
// If password is incorrect, ModelState will be invalid now
}
if (ModelState.IsValid)
{
// save posted data
}
It sounds like you'd ideally want an action which you can call asynchronously from the client. While this can take the form of a standard MVC controller action, you may want to consider building this into a Web API controller (Generally we would use Web API controllers to serve up non-HTML responses). You can read more about Web API in many places on the web so I won't go into that now, but let's say you have a HttpResponseMessage result method which looks like this:
[HttpPost]
public HttpResponseMessage CheckCredentials(string username, string password)
{
// Check user credentials and return either one of the following results:
// If credentials valid
return Request.CreateResponse(HttpStatusCode.OK);
// If not valid
return Request.CreateErrorResponse(HttpStatusCode.BadRequest);
}
Using this pattern you could return a '200 OK' response for valid credentials and a '400 Bad Request' for invalid credentials.
As you already stated, you could have the HTML content required for authentication prompt hidden on the page. When the user performs an action which requires authentication, you could render the popup. When the user submits the popup you could fire off an asynchronous request to the Web API endpoint which you created earlier. Depending on the response you get back, you either proceed with the task or prompt for credentials again with an error message.
Obviously as you'd be sending user credentials over a we request, make sure you're making use of HTTPS.
EDIT:
As Chris mentioned below, this solution leaves your 'quick check' in the hands of the client. While this is fine when you simply want to provide a way to stop the user from easily carrying out an action without re-entering their credentials, you should not rely entirely on it.
You could store the username and password as hidden fields and include them with your main synchronous POST. This would allow you to check that the user entered valid credentials from the server.

Anti-forgery token is not reused

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(…)

forms authentication fails for POST request, .Net MVC

In my .Net MVC application, I have default controller, which anyone should have access (as it contains the welcome page, register, login, password reset methods etc). All the other controllers have been inherited from a controller with authorize attribute.
I can browse to default/index, and default/register pages(i.e. any request of GET type). But any POST request to a default controller action acts like they need authorization. For example, when the user enters email/password and click login, it makes a POST request to default/login.
I tried with setting location paths in webconfig but still no luck. I have noticed that every POST request returns the form authorization cookie with empty in its value, while GET requests do not send back auth cookie.
Any idea what I have missed?
Thanks a lot in advance :)
Anuruddha
You can try with this
[HttpPost]
[AllowAnonymous]
public ActionResult Index()
{
//...
}

Reposting data through a generic method that is independent of form model in ASP MVC

This is the client's request: in some cases there are several forms that don't require authentication or it takes too long for a logged user to finish completing a form and the session expires. In theses cases he wants to retain the data when the user submits the form by serializing and storing it in a SQL table and then, after the user (re)logs in, he is redirected to the respective form and that is repopulated with the data retrieved from the database and deserialized.
The problem is that he wants all the logic of the storing, retrieving and resending the data in the authorization code block. I know how to do the storing, serialization, retrieving, deserialization of data and the user redirection to the respective page, but I don't know hoe to make it generic so that it works for every model on every form.
The client does not want any code for this task done in the form action method. For example:
[HttpPost]
[Authorize]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
post.CreatedBy = (Guid)Membership.GetUser().ProviderUserKey;
post.CreateTime = DateTime.Now;
repo.Add(post);
repo.Save();
return RedirectToAction("Index");
}
else
{
return View(post);
}
}
As you can see he wants to keep it as clean as possible. He had this suggestion:
The server receives a HTTP request in RAW text format. Using this text, it builds the objects (RequestContext, FormCollection collection, etc. etc.). So, you should be able to build in a hook and e.g. save the raw request. After succesfull login, this previous raw text of the HTTP request could be injected in the handling.
I don't really know how to do that or even if it is possible in MVC.
If someone can help I'll be extremely grateful.
Thanks,
ABTeam
The proper way to do this is to capture the user's progress in the database, and provide a mechanism for returning them to the next step in the process. This can be done with a ?step=n parameter in the URL. If the user gets logged off, they can log back in and be returned to the correct step in the process.
Your client's request for doing this in the authorization code block is not an appropriate use of functionality. That's not the purpose of the authorization block, and attempting to do business logic there, in the manner that the client describes, will almost certainly compromise security and result in unmaintainable code.
The authorization block is not the right place for it, but you may be able to do something fairly generic with action filters.
Load the saved data in OnActionExecuting. I'm not sure if you'll be able to get it passed to the action method as a parameter, but at the least you should be able to add it into ViewData so it can be used as a starting point for generating the model for the form page.
Not sure if the model will be available for saving before ActionExecuting, but if not the model as it exists after the action method runs should be an appropriate alternative.

Resources