Don't trust the URL... since POSTed data may hide what is really going on.
The URL should be treated as a client-side value and should not be trusted anyway. Anyone can easily alter the URL and could easily create a POST to any UserID
.
Anyone who can guess the route variable names in ASP.NET MVC, can put unexpected data into a controller
This can happen anyway by parameter tampering. There are some useful pointers in this guide: http://msdn.microsoft.com/en-us/magazine/hh580736.aspx
Specifically
What Is It? Parameter tampering is an attack in which parameters are altered in order to change the application’s expected functionality. The parameters may be on a form, query string, cookies, database and so on. I’ll discuss attacks involving Web-based parameters.
How Is It Exploited? An attacker alters parameters to trick the application into performing an action it wasn’t intending. Suppose you save a user’s record by reading her user ID from the query string. Is this safe? No. An attacker can tamper with a URL in your application
In MVC this can happen via Model Binding:
Model binding is a great feature of Model-View-Controller (MVC) that helps with parameter checks as the properties on the Order object will automatically be populated and converted to their defined types based on the form information. ... Just be careful to limit what properties you allow to be populated and, again, don’t trust the page data for important items. ...
Note that I use the [Bind(Exclude)] attribute here to limit what MVC is binding into my Model in order to control what I do or don’t trust. This ensures the UserId won’t come from the form data and thus can’t be tampered with.
So you should also be careful that form parameters that don't exist in your view cannot be tampered and added by a malicious user. You would do this by adding the Exclude
attribute:
public ActionResult Edit([Bind(Exclude="UserId")] Order order)
Is this behavior consistent across other MVC frameworks (Rails etc)?
Not sure, but I don't see it as a flaw as permission checks should be made on POSTs as well as your GETs. You application should not assume that a URL that has been POSTed to has permission to do this just because that of the false assertion that the URL with the form on must have been loaded first.
What are some ways to guard against this type of unexpected results? Should every POST be cross checked with URL parameters?
You code should just decide to take either the route variable or the POST variable (if it's provided to you from MVC as a method variable named UserID
then why not use that, it doesn't matter if it was from POST data or the URL as long as you are consistent), and then make all logic decisions based on that. This includes the initial authorisation and also the processing and business logic - they should all use the same origin of the value to make sure actions are only carried out when authorised.