I somewhat understand how [ValidateAntiForgeryToken] prevents CSRF and I have read through this question, but I'm unsure whether this would prevent someone from falsifying the parameters for a form post.
ItemList has an Items property that is a collection of items and a User property that is a reference to the ApplicationUser that it belongs to. Item has an ItemList property that is a reference to the list it belongs to. Here are the Add methods in the ItemController:
// GET: Item/Add/4 (Adds new Item to the ItemList with ID=4)
public ActionResult Add(int? itemListId)
{
// Gets the current user and the ItemList that the Item will be added to
UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
ApplicationUser currentUser = userManager.FindById(User.Identity.GetUserId());
ItemList itemList = db.ItemLists.Find(itemListId);
// Makes sure that ItemList exists and belongs to the user
if (itemList == null || itemList.User != currentUser)
{
return View("InsufficientPerm");
}
ViewBag.ItemListId = itemListId;
return View();
}
// POST: Item/Add/4 (Adds new Item to the ItemList with ID=4)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add([Bind(Include = "ID,Name")] Item item, int? itemListId)
{
if (ModelState.IsValid)
{
ItemList itemList = db.ItemLists.Find(itemListId);
item.ItemList = itemList;
db.Items.Add(item);
itemList.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
My question is whether [ValidateAntiForgeryToken] would prevent a user from falsifying the itemListId parameter during the post, or if I would need to put another if (itemList == null... check in the post method.
Edit: Here is the logic that I am looking at right now:
The use of ValidateAntiForgeryToken forces a user to access the first method (therefore loading the view) in order for a post to be accepted. If they don't load that view, then there will be no anti-forgery token.
The user will go to the form webpage (let's say http://foo.bar/Item/Add/3)
The user will fill out and submit the form, which would call the post method (in this case itemListId=3 because that's the webpage that was accessed)
There is no way for a user to pass a different itemListId to the above step, because it is passed by the webpage when they submit the form
Now, please let me know if there is something wrong with what I have stated above, or if that logic is correct (meaning that I don't need to check the validity of itemListId during the post). PLEASE give an example or a link to clarify where my logic is incorrect
There is no way for a user to pass a different itemListId to the above step, because it is passed by the webpage when they submit the form
This is false. The AntiForgeryToken will not protect you from altered data.
Make a GET request to Item/Add/4. I assume you will have a form on this page. You can include the #Html.AntiForgeryToken().
Use your browser's debug tools to inspect the form.
Now you can edit the action attribute to directly modify Item/Add/5 the value.
Or modify any <input> field value.
Put a debug break on your POST action and you will see the altered values when you submit the form.
The above is not the only way to tamper with the data. Therefore, you should always validate any input.
[Authorize, ValidateAntiForgeryToken]
[HttpPost]
public ActionResult NukeMyBankAccount(int accountId)
{
var account = db.GetAccount(accountId);
// validate
if (CurrentUser.Id != account.Owner.Id)
{
return RedirectToAction("Unauthorized");
}
else
{
db.NukeAccount(accountId, areYouSure: true);
}
...
}
No. The ValidateAntiForgeryToken attribute has absolutely zero impact on the values each form field may contain. It also has absolutely no idea what data belongs to which user.
ValidateAntiForgeryToken dont prevent falsify, it prevent that the request becomes from another place.
The ValidateAntiForgeryToken check if __RequestVerificationToken exists in the form and validate it, so whatever params is in the body of the request, if the __RequestVerificationToken is invalid will break and dont returns a 200 OK, so to use it just put the Helper #Html.AntiForgeryToken() inside the form and the [ValidateAntiForgeryToken] filter in the action.
So see this article about that: How AntiForgeryToken works
The CSRF attack consists in deceive victim to click in link or load a image where the URL is of a system that victim is logged, but attacker do not know the victim credentials.
The simple case, the attacker know that the HTTP GET request in the target system change some information. To attack, just send the e-mail or link to a user of the target system with a image where the URL point to the target system.
To prenvent this, a good pratice is do not create URL that make changes in data of the system through HTTP GET request.
In complex case, the attacker create a page with a form whose action point to a URL of the target system with POST method.
In this case, use the ValidateAntiForgeryToken is fundamental. Because the token generated by the target system is known only by the target system and can be validated.
So, the ValidateAntiForgeryToken only validate if the request was originated from the target system. Don't have any relationship with others parameters in the form.
Related
Can I run these get and post actions with just single button? I get an id from my view then I just wanna run my post action.
I've already tried to getting id with parameter and put it in session but couldn't do it with one submit button
[HttpGet]
public ActionResult JoinEvent(int id)
{
Session["EventID"] = id;
return View();
}
[HttpPost]
public ActionResult JoinEvent(EventPerson eventPerson)
{
Person per =(Person) Session["Login"];
eventPerson.PersonID = per.PersonID;
eventPerson.EventID = (int)Session["EventID"];
eventPerson.TotalPerson = eventPerson.TotalPerson + 1;
eventManager.Join(eventPerson);
return View();
}
and this is my view
<td class="buy_link">
#Html.ActionLink("Join","JoinEvent","Join",item.EventID,null)
</td>
As what you posted here, it looks like the EventId can be set freely (without any security restrictions) by the client by simply navigating to GET /JoinEvent?id=100.
If security isn't an issue, you can do it by many many ways
Passing the event id to the client, which in turn will give it back to the server.
How ? Here is one way
Passing it as part of EventPerson model, which you will need to add a new property inside of that model.
ActionResult should: return View(new EventPerson { EventId: id })
Decorating view top row (after import statments) with the following #model EventPerson, this will tell the view that now he represents this type. (Which was passed by the GET action)
Now we should decide if we want to pass it back to the server as query string parameter or a body parameter ? Because we're doing a semantic "Insert" action, in most cases the practice asks us to pass it as part of the body. thus, you should use an hidden text box to pass EventId parameter.
#Html.Hidden("EventId", #model.EventId))
"EventId" is the property we want to bind on our POST server action EventPerson newly property. (Point#1)
#model.EventId is the value we took from the server and passed it into the View (Point#2)
Technically you cannot. When you create an anchor tag using action link, it creates a link to the specified URL. When you click on that link that fires a GET request. That will not POST data to the action method. If you need to POST data, you need to either submit a form or you can use Javascript/jQuery to do that. You can collect the data and send it as a POST using Js/jQuery. But just the anchor won't send a POST method.
Refer this
So I've always used the loose PRG pattern where you return a view on ModelState validation failure from a POST action. However, it's always bothered me that I have to build the model not only in the GET action, but also rebuild it again in the POST action on failure. I've used different methods of doing the rebuilding using "view model builders" or just a private function within the controller that builds the view model for both actions, but these still bother me as well.
After reading this article by Ben Foster (http://benfoster.io/blog/automatic-modelstate-validation-in-aspnet-mvc), it makes a lot more sense to just rely on the GET action to build your view model - keeping it one area of the code - and then use the necessary action filters to save the ModelState for rendering when you are redirected back to GET on a failed POST.
So I've implemented that using the filters Ben mentions in his article like below. However, I am curious what happens if the user refreshes after they have been redirected back to the GET on a ModelState failure? How would I differentiate between someone accessing the GET directly versus a ModelState failure? Currently the ModelState would be gone if a user refreshes at that point. Is that the correct action, or should the user continue to see the errors until they POST with valid data? Essentially, should they be seeing the data that is in the database, or should they continue to see the changes they made when POSTing?
[ImportModelStateFromTempData]
public ActionResult Edit(int id)
{
// in a real application this would be retrieved from the db
var editView = new EditView()
{
UserId = id,
Name = "John Doe",
Age = 20,
Message = "Hello world"
};
return View(editView);
}
[HttpPost]
[ValidateModelState]
public ActionResult Edit(EditCommand editCommand)
{
// save to db here in real application
return RedirectToAction("Success");
}
I use the same [ImportModelStateFromTempData] filter in a couple projects, and it works great.
In my opinion, if the user refreshes, you shouldn't preserve any model state errors. The user is asking for a fresh view of that page, and it'd be frustrating to never be able to get a clean view. In same vain that refreshing after a POST shouldn't resubmit a form, refreshing after a GET shouldn't preserve a POST.
I've got two controller actions at the moment: one that displays a strongly typed view, based on an unique identifier and another one that changes the model. Here's some code to visualize what I mean:
[HttpGet]
[ActionName("Edit")]
public ActionResult UpdateDevice(string code)
{
// Request the device based on the code.
var device = GetDeviceModel(code);
// Present the device in a view.
return View(device);
}
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
}
The code identifies the device, but it is also possible to change it. And that's my problem: Inside the post method I can access the new code using model.Code, but I also need to know the old code, to be able to change it.
I tried several alternatives, but none of them did satisfy my requirements:
ViewData does not get persisted until the post.
TempData is based on Sessions or Cookies – none of them I want to use at the moment.
Hidden fields and model bindings are not an option, because they can be manipulated on client side.
Finally I tried requesting data from the query string like this:
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
var oldCode = Request.QueryString["code"];
}
And this works! But I haven't found any resources on this around the web. So my question is: Is using the query string inside a post action safe against modifications? And what (if there are any) are the conditions for this to work?
If this is not a valid way to "remember" the code, are there any alternatives?
Based on the requirements you have mentioned in your question, it seems that you are looking for safety in the case. So I would say that QueryString is not safe. In my opinion using Session variables is the appropriate method that keeps your critical data in a safe location. You can use the method like this
[HttpGet]
[ActionName("Edit")]
public ActionResult UpdateDevice(string code)
{
Session["code"] = code;
....
}
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
if (Session["code"] == null)
throw new Exception("Error Message.");
var code = Session["code"].ToString();
....
Session["code"] = null;
}
I think you can make another property inside DeviceModel name OldCode.
and in your view you can save this value in hiddenfield like this
#Html.HiddenFor(m=>m.OldCode)
Now in controller post method you can get both the values like this.
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
var oldcode=model.OldCode;
var newcode=model.Code;
}
Hope this helps...
Nothing submitted via a GET or a POST request is safe from modifications. While a GET query string is obviously easy to modify, it doesn't take much effort to spoof POST variables either. It sounds to me like you need to re-think your approach.
Optimally, you would do permission checking server-side to determine if the user is allowed to update the device with the passed (old) code. If they do not have permission, return an error such as an HTTP 550 code.
If you truly can't support this approach, I would suggest adding an "OldCode" field to the DeviceModel class as suggested by others, but encrypting it before sending it to the client. Then you can safely write it to a hidden field, and decrypt back on the server without fear of the user changing the value.
I have a layout page that displays simple messages to the user.
I have a People Controller with an Index action/view that displays a list of people and I have an Edit action/view for a person.
When the user successfully updates and saves a person's details, I want to redirect from the Edit action to the Index page but display a message such as "The person was successfully updated".
I've tried adding things to the ViewBag but it doesn't persist.
I have a ViewModel Base class, but don't know if/how to set the Index's ViewModel from the Edit action.
How can I achieve this where messages are passed from various places to be used in _Layout?
You can use TempData to accomplish this. Values stored in TempData will only persist across one Http request, making it ideal to store these types of messages.
Edit POST Action
[HttpPost]
public ActionResult Edit(EditPersonModel model)
{
// .. Your code to edit the person ..
TempData["message"] = "The person has been updated.";
return RedirectToAction("Index", "People");
}
Your View
#if (TempData["message"] != null)
{
<p>#TempData["message"]</p> // Displays the message
}
You can build an entire notification system that uses TempData and ViewData (controller extensions, etc), but this is the simplest way to do it.
I am learning MVC 3 after hours right now and last night I ran into an issue that seems like it would be very simple to solve but I can't seem to locate a solution for it.
I have a simple contact form. The end user fills out the form and submits it. When they submit the form I redirect the end user to a different action in the same controller which renders an "inquiry submitted" page/view which is basically a "Thank you page".
The controller is setup like so.
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactInfo contactInfo)
{
if (!ModelState.IsValid)
{
return View();
}
//perform some business logic
return RedirectToAction("InquirySubmitted",contactInfo);
}
public ActionResult InquirySubmitted(ContactInfo contactInfo)
{
return View(contactInfo);
}
The problem:
I do not want end users navigating directly to the InquirySubmitted action via the browser.
I only want the ContactUs action in the controller to be able to send users to the InquirySubmitted View.
I have attempted to make the InquirySubmitted action private so that only the controller can call it like so:
private ActionResult InquirySubmitted(ContactInfo contactInfo)
But this produces an error which I fully understand because I am forcing the browser to request InquirySubmitted by using RedirectToAction().
So my question is simply: What is the best "MVC 3 style" solution to this issue.
You will need to put logic in your InquirySubmitted ActionResult in order to prevent users from viewing the page if they are not supposed to.
You are already passing the InquirySubmitted method your model (ContactInfo). Could you simply inspect the data passed to the method and if it is absent then redirect the user to an error page (or some other page of your choice)?
An alternate solution would be to set a boolean in session that indicates that the user completed the "ContactUs" form. Then you could check for that session object within InquirySubmitted.
First, I would have to say.. Who cares if someone can navigate directly to the Inquiry submitted page? Is there any confidential information, or something sensitive there? If not, so what? What does it hurt?
However, if you're determined to do so. The answer to your question of "How to make an action not accessible directly from the browser" is that You can simply use Html.Action() to render the page, and then decorate the action method with a [ChildActionOnly] attribute.
This doesn't actually solve the problem though, since making the action indirectly accessible only answers your question, not solves your problem. Ultimately, you need to redirect the user to a url to load the page, so you will need some logic that determines if they can view the page or not. This is
Not sure if this still applies in MVC3, but in MVC2 it worked.
your global.asax file has your url structuring in it. You can add your InquirySubmitted to the list of urls that isn't accessible there.