MVC public ActionResults accessiblity from URL - is it safe? - asp.net-mvc

I have being programing in MVC for a couple of months, recently I've been requested to revisit our authorization mechanism.
There is an attribute that checks weather a user can access an action result, but this attribute doesn't appear above all the public actions. There are plenty of JsonResults and other ActionResult actions which can be accessed from the URL bar.
My question is weather its worth adding that attribute to all the action results
or make some sort of attribute that won't allow access to the action unless its an ajax request or a post?

There is no functional difference between a browser request and an ajax request, they both come through the same http pipeline. Matter of a fact, sometimes executed outside of the forms authentication scheme, so adding [Authorize] attribute could block valid requests as well.
I would create some sort of authorization that requires the use of a header value of some sort to distinguish access to your Json methods.
You could add the header to every ajax request that was passed, but that would be easily visible in a program such as Fiddler or Firebug. You could use constantly changing authorization keys, but I think the headache of overhead would be at a higher cost than the data you're likely trying to protect.
You could decorate your methods with [AcceptVerbs(HttpVerbs.Post)] but again, those can be easily remedied using a program like Fiddler or Postman (chrome add-on)

You can use Request.IsAjaxRequest to ensure that the request came from an AJAX request. This will verify that the X-Requested-With header is present in the request, which is not present in a request that is made via the address bar.
What is the threat that are you trying to guard against? Checking the above is good to stop casual users from reading your JsonResults but ultimately you cannot protect public ActionResults or JsonResults because if someone wants to capture your response they can simply make their own request using curl:
curl --header "X-Requested-With: XmlHttpRequest" www.example.com/controller/method

Related

AJAX CSRF attacks and .Net MVC

This post describes a tokening system for all JSON HttpGet and HttpPost AJAX calls:
In short, you use the AntiForgeryToken attribute to create a token on the page, and then manually validate that that value is sent back to the controller via the AJAX call.
http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks
After hours of internet sleuthing, there are always references to this possibility, but no one actually implements it. Instead the commonly repeated techniques are 1) using only HttpPost AJAX requests (which breaks REST), 2) wrapping all json responses in an anonymous object, which results in significantly inelegant code in .net MVC4, 3) using an unparsable cruf, which breaks common libraries such as backbone.js
So why aren't we all using the tokening system linked above? What am I missing?
If you're concerned about CSRF, and your concerned about REST and doing REST correctly, then you shouldn't be doing anything in a GET that would be affected by a CSRF, since the entire purpose of AntiForgeryTokens is to deal with changing data (ie, you can't use an AntiForgeryToken without first getting the page anyways that contains the token).
So, saying using POST "breaks REST" seems to be misunderstanding what you're using the token for in the first place. It's true that a GET can expose sensitive information, but you have to have some way to get the token first if you want to use it with GET.
The real problem with Ajax and AntiForgeryToken with json and Ajax is that the "built-in" validation only works with form values, not json. So you have to do the validation yourself, and that article you linked to gives a good explanation of how to do that.

How to make WebAPI actions accessible only from my app?

A common use case for WebAPI would be to have shell views rendered by MVC controllers, which contain javascript that then hit your API to access data.
But let's say you have some expensive API operations and you don't want people remotely accessing those endpoints -- you only want your MVC views, delivered by your application, to access them. How could you go about protecting them?
In this case Request.IsLocal doesn't work, because javascript is invoking it from the client's browser on their machine. Even if it did work, you need to dig to get the real HttpContext in order to find this property -- and that solution wouldn't work in self-hosted WebAPI.
For API endpoints that require a valid IPrincipal, you could protect them with the [Authorize] attribute. But what about API endpoints that you want your app to be able to access for anonymous users?
I have tried a solution and will post it separately as an answer, because I'm not sure if it's the best (or even a good) approach.
If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.
Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.
If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.
Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.
Create an MVC action filter and add it as a global filter during Application_Start.
Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.
The global MVC filter does this:
Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.
If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.
The WebAPI filter does this:
Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.
If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.
Otherwise, throw a 403 Forbidden exception.
A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.

ASP MVC 3 RequireHttps attribute change all links to https

I have an ASP MVC 3 website that has a Feedback form and should require SSL.
Now, I have an action called Feedback inside a controller called 'ContactUs' that is responsible for viewing and processing the feedback.
When I used the [RequireHttps] attribute on that action, it works nice and it changes the URL to "https". However, I noticed that all the links inside my page are now pointing to "https"! As if this attribute had forced the routing engine to apply the same to all links!!!
Of course, the SSL is only required for this single action and all the rest need to have normal http.
Could anyone tell me how to solve this?
In your case [RequireHttp] attribute might be OK if you clear out the login cookie - or you'll be sending it in clear-text across the wire. It might be more work than it's worth to avoid the slight cost of further HTTPS calls. SO is all about recycling questions and other users reading your question might think it's OK to drop down to HTTP after login, when it's usually the wrong thing to do.
The [RequireHttps] attribute can be used on a controller type or action method to say "this can be accessed only via SSL." Non-SSL requests to the controller or action will be redirected to the SSL version (if an HTTP GET) or rejected (if an HTTP POST). You can override the RequireHttpsAttribute and change this behavior if you wish. There's no [RequireHttp] attribute built-in that does the opposite, but you could easily make your own if you desired.
There are also overloads of Html.ActionLink() which take a protocol parameter; you can explicitly specify "http" or "https" as the protocol. Here's the MSDN documentation on one such overload. If you don't specify a protocol or if you call an overload which doesn't have a protocol parameter, it's assumed you wanted the link to have the same protocol as the current request.
The reason we don't have a [RequireHttp] attribute in MVC is that there’s not really much benefit to it. It’s not as interesting as [RequireHttps], and it encourages users to do the wrong thing. For example, many web sites log in via SSL and redirect back to HTTP after you’re logged in, which is absolutely the wrong thing to do. Your login cookie is just as secret as your username + password, and now you’re sending it in clear-text across the wire. Besides, you’ve already taken the time to perform the handshake and secure the channel (which is the bulk of what makes HTTPS slower than HTTP) before the MVC pipeline is run, so [RequireHttp] won’t make the current request or future requests much faster.
You can create another custom filter attribute to move back to http. Try solution from this question...
Why once SSL is enabled with [RequireHttps] at action level, it remains enabled forever?

asp.net mvc 2 -- losing authorization when RedirectToAction with JSON data

I'm refactoring some MVC code that originally used POST'ed form data. The form's fields are serialized using jquery's serialize() method and sent to an MVC controller Save Action that checks things out and redirects as appropriate (if errors in form values, redirect to the Edit Action, if fine then save and redirect to the Display Action). All actions are invoked via AJAX and return Partial Views. Everything works grand. Note: The site uses AD-based authorization, so users are prompted for their windows credentials upon first loading the site, but are never prompted again.
However, I'm now looking to interact with the server via JSON objects instead of form fields. Granted, I serialize the JSON object on the client and, with the aid of an imported MVC2 Futures/MVC3 class JsonValueProviderFactory, am able to correctly model bind the sent JSON object to a C# class in the Controller's parameters.
I maintain the same logic, but things start to blow up when I try to return a RedirectToAction ActionResult when the Controller accepts JSON objects. I lose authentication, the user is prompted for their credentials again, and I find myself in a infinite loop on the originally requested Action (save). Every time the user is prompted for credentials and simply runs through the Save Action again. The end result for the user is an unending alerts prompting for login credentials. Neither of the actions specified in the RedirectToAction calls are ever hit.
Can the fact that the original request uses a JSON contentType be interfering with the behavior of RedirectToAction? That's the only thing I can think of as it works fine when I don't use JSON to post and it works fine when I return PartialViews instead of using RedirectToAction. The infinite repeat of the Controller Action and continual loss of authorization credentials seems to suggest that RedirectToAction is not the way to go in this situation.
I can post code on request. I am also successfully handling stuff like copying the ModelState over to TempData and other RedirectToAction tricks. Again, it DOES work when using a non-JSON solution. Any insight is greatly appreciated!!
EDIT WITH FOLLOW-UP INFO:
Turns out, I get an "Unauthorized" error even when I completely disable NTLM authentication/authorization for the site. IIS server doesn't look for any authorization, web site doesn't look for any authorization, yet the error when trying to Redirect with JSON contentType request still occurs and complains of being "Unauthorized". This is WEIRD.
To update everyone, I haven't found a solution nor do I know for-sure what the situation is. However, I'm willing to bet it has to do with the fact that RedirectToAction issues http GET requests and the action I'm redirecting to only accepts POSTs. Even if I remove the restriction, it's still sending JSON data and it still needs to be done by POST.
In short, RedirectToAction with JSON data appears to be fundamentally undoable. You need to POST JSON data but RedirectToAction emits GET requests. That's my going theory, at least. =)

HTTP Referer header in Struts 2

How can I get the Referer header under Struts2? Right now I'm using an ActionSupport class and I can't seem to get a ServletActionContext object or implement the ServletRequestAware interface? (Where is ServletRequestAware in Struts2? which jar?)
I'm trying to set up an automatic redirect to a page's referer, stored in a session variable. When someone requests OAuth authentication, I store the referer in session and then send them to twitter. When they click allow, twitter sends them to my OAuth callback url. I do work there (persist access token) and then would like to send them to the referer I've stored in their session.
As I'm sure you can tell, I'm very new to Struts. I did spent over three hours reading Java docs, googling and otherwise trying to avoid wrath against me the noob cringe
Thanks!
Its considered "uncool" to reference HTTP elements in actions, but it becomes necessary so....
org.apache.struts2.interceptor.
ServletRequestAware.setServletRequest(HttpServletRequest request);
implement the interface, create a HttpServletRequest member variable and then set your member variable to the request in the implemented setter above. Now you have the request and you can do your request.getHeader("referer").
Some people also use the static method org.apache.struts2.ServletActionContext.getRequest() to get the request. Its considered bad form because it can make unit testing more difficult, but I see it all the time.

Resources