ASP.NET MVC ValidateAntiForgeryToken — can it be replaced with authorization check & referrer check? - asp.net-mvc

In ASP.NET MVC there is a ValidateAntiForgeryToken attribute, that enables cross-site scripting defence.
Is it possible to replace this mechanism with
authorization check, including checking that resource, that is being manipulated, belongs to current user;
referrer check, that will forbid AJAX web api requests from external hosts;
inhibition of site hosted in iframe?

This does not prevent cross site scripting, only cross site request forgery.
authorization check, including checking that resource, that is being manipulated, belongs to current user;
No, because the resource does belong to the current user, it is only the request that has not been willingly made by the current user.
e.g. say on your website www.foo.com you have the following URL that will delete the user's account.
www.foo.com/DeleteAccount
Your user is logged into www.foo.com. Now say your user visits www.evil.com which includes the following image tag on the page.
<img src="http://www.foo.com/DeleteAccount" />
This will make a request to your page and delete the user's account because the DeleteAccount resource will have checked authorisation via cookies and determined that the user is indeed authorised because the auth cookie was supplied with the request.
referrer check, that will forbid AJAX web api requests from external hosts;
Yes, this is a valid check although it is weaker than the method of using the Anti Forgery Token as mentioned in your question.
The OWASP CSRF Cheat Sheet states
Although it is trivial to spoof the referer header on your own browser, it is impossible to do so in a CSRF attack. Checking the referer is a commonly used method of preventing CSRF on embedded network devices because it does not require a per-user state. This makes a referer a useful method of CSRF prevention when memory is scarce. This method of CSRF mitigation is also commonly used with unauthenticated requests, such as requests made prior to establishing a session state which is required to keep track of a synchronization token.
However, checking the referer is considered to be a weaker from of CSRF protection. For example, open redirect vulnerabilities can be used to exploit GET-based requests that are protected with a referer check. It should be noted that GET requests should never incur a state change as this is a violation of the HTTP specification.
There are also common implementation mistakes with referer checks. For example if the CSRF attack originates from an HTTPS domain then the referer will be omitted. In this case the lack of a referer should be considered to be an attack when the request is performing a state change. Also note that the attacker has limited influence over the referer. For example, if the victim's domain is "site.com" then an attacker have the CSRF exploit originate from "site.com.attacker.com" which may fool a broken referer check implementation. XSS can be used to bypass a referer check.
Also note that sometimes the referer isn't always passed as the user may be using privacy software that removes the header.
inhibition of site hosted in iframe?
This can be a valid defence for widgets that you host to be included on other sites.
e.g. www.bar.com could include your widget on their page via the use of a script tag:
<script src="//www.foo.com/widget.js"></script>
In order to prevent www.bar.com from submitting the form within your widget, your JavaScript code would document.write an IFrame into the page and then include your content within that. The Same Origin Policy will prevent the IFrame content from being read by the parent page and your form could not then be submitted by the site that includes your widget. However, here you may need a manual confirmation window to pop up in the case of any clicks to prevent click jacking attacks (e.g. if you had a like button (similar to Facebook) and you wanted to prevent fake likes from the including page submitting your form automatically).
OWASP Recommendation
The OWASP Recommendation is to use the Synchronizer Token Pattern which is the one implemented by ASP.NET MVC with ValidateAntiForgeryToken.

Related

Cross-site request forgery token viewable in source code

Should my Cross-site request forgery TOKEN be viewable on my web page source code
I am running a rails app in production and can see Cross-site request forgery token i am guess it should NOT be viewable
There's no way to put it on the web page without it being viewable. If you can't put it on the web page then you can't use it. There's no "secret" part of the web page which isn't in the source, or headers, both of which can easily be viewed. So, logically, if it is to have any function whatsoever, it must be viewable.
In order for it to function as part of a security system, therefore, the security of the system must NOT rely on nobody being able to see what the token is, and that is indeed the case. The security of the system depends on the token matching a stored value server-side: in other words, it means that the token provided with the form, ie sent from the server to the client, needs to match the one submitted BACK TO THE SERVER by the form.
Read this: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet

How to protect against CSRF

How can I protect my website against Cross-Site Request Forgery attack?
I am visiting a "normal" website. (f.e. normal.php)
In the background it loads another website (f.e. victim.php/send_comment) where I'm already logged in.
The website fills the comment boxes of the victim.php with JS and automatically send the request.
In the web I always find the trick to use tokens against CSRF. But in this example, the website normal.php will get the token, when it loads the other website.
Am I misunderstanding how the token works? If not, how can i prevent my site from accepting this request?
The whole idea of CSRF is that you can't get victim.php/send_comment without a token from a previous page you've visited.
You form a "chain" of requests from your initial login until you get there, where each request is authorized by the previous one - unless you intercept the login page, there should be no way to forge requests.
The easiest and safest way of doing this is just using a web framework that handles CSRF for you. Doing it by hand is probably unnecessary and error-prone.

Does RedirectToAction pose a security risk?

I have an HTTPS post coming in via a secure form. Without going into lengthy explanation: I need to call an action within the same controller that accepts two tokens passed as parameters. When I run Fiddler, I see that that method is being called with the parameters in the URL. My question is: Does this pose a security risk? Is there a more secure way of redirecting within the same controller?
Yes, it poses a security risk, but it is easily mitigated by simply validating that the urls you are redirecting to are within the same domain as your source destination.
In fact, this is on the OWASP top 10.
A10 - Unvalidated Redirects and Forwards
EDIT:
I just realized that I missed the "ToAction" part of the question, so no.. It's not really possible to redirect outside of the site with RedirectToAction, so there isn't a worry for that. However, if you are using direct user input to feed into your RedirectToAction (and that includes accepting post data that you generate in a different page) then it's possible that an attacker could redirect to a method you did not anticipate. However, this is no different from a user simply trying random URL's and hitting one, or knowing a url and going to it manually. You need to have authorization in place to prevent access to URL's that the user does not have authorization to view.
If the original Action is accessed via HTTPS then RedirectToAction will redirect to a relative URL on the same domain using the same protocol.
So if your original page is
https://www.example.com/Foo/Bar
and this redirects to the FooBar action with some route parameters:
https://www.example.com/Foo/FooBar/1/2/3
an attacker cannot read the parameters 1/2/3 nor the rest of the URL.
However, the things you should bear in mind are:
The URL parameters will be logged by default by the browser (history), your server, by corporate proxy servers and possibly by other devices on your network by default.
If the user follows any links from your page to other https URLs, the referer HTTP header will contain your page address including parameters. Modern browsers will not send the referer header with http links though.
If there are any other https resources on your page this will cause the browser to send the referer header with the request.
For these reasons, if your parameters (1/2/3) are private, then you may wish to POST this data to the target page rather than use RedirectToAction (which results in a GET).
Note that you should be validating that the current user has access to the resources that 1/2/3 refers to (e.g. if the parameters were an order ID, you should check that the user identified by their auth cookies allow them to see the order referenced). Keeping 1/2/3 private are only beneficial if the parameters are themselves sensitive (e.g. a social security number).
Note that the OWASP Top 10 vulnerability, "A10 - Unvalidated Redirects and Forwards" does not apply here as RedirectToAction can only redirect to another action. If the other action redirects to a user set URL, then the vulnerability would lie there instead.

Rails 4 skipping protect_from_forgery for API actions

I've been implementing a Rails 4 application with an API. I want to be able to call the API from mobile phones and the webapp itself. I came across this note while researching protect_from_forgery:
It's important to remember that XML or JSON requests are also affected and if you're building an API you'll need something like:
class ApplicationController < ActionController::Base
protect_from_forgery
skip_before_action :verify_authenticity_token, if: :json_request?
protected
def json_request?
request.format.json?
end
end
I was thinking of doing this, but I have some reservations/questions:
This solution seems to leave the CSRF hole open because now an attacker could craft a link with an onclick javascript that posts JSON?
Would checking for an API token be a reasonable substitute? i.e., what if instead of skipping the authenticity check, allowing it to fail the check and recover in handle_unverified_request if the api token is present and correct for current user?
Or maybe I should just make the webapp and mobile devices send the CSRF token in the HTTP headers? Is that safe? How would the mobile phone even obtain the CSRF token, given that it isn't rendering HTML forms to begin with?
Edit for clarification:
I am more concerned about the webapp user clicking a crafted CSRF link. The mobile users are authenticated, authorized, an have an API key, so I am not concerned about them. But by enabling CSRF protection for the webapp users, the mobile users are blocked from using the protected API. I want to know the correct strategy for handling this, and I don't believe the Rails documentation gives the right answer.
An attacker could CURL at your controllers all they like, but if your API requires authentication, they wont get anywhere.
Making the API consumers send a CSRF is not really what CSRF does. To do this you'd need to implement a type of knocking mechanism where your client hits an authorization endpoint first to get the code (aka CSRF) and then submit it in the POST. this sucks for mobile clients because it uses their bandwidth, power, and is laggy.
And anyway, is it actually forgery (i.e. the F in CSRF) if its an authorized client hitting your controller after all?
Sending the CSRF token in an HTTP header is indeed a common approach. It ensures that the client has somehow obtained a valid token. For example, a crafted CSRF link will be sent with credential cookies but the header will not include the CSRF token. Your own javascript on the client will have access to domain cookies and will be able to copy the token from a cookie to the header on all XHR requests.
AngularJS follows this approach, as explained here.
As for your first two questions:
This solution seems to leave the CSRF hole open...
Indeed, which is why you should not disable the CSRF token also in your API.
Would checking for an API token be a reasonable substitute? ...
Probably not. Take into consideration the following (from OWASP):
CSRF tokens in GET requests are potentially leaked at several locations: browser history, HTTP log files, network appliances that make a point to log the first line of an HTTP request, and Referer headers if the protected site links to an external site.
General recommendation: Don't try to invent the wheel. OWASP has a page called REST Security Cheat Sheet as well as the one I linked to before. You can follow the Angular approach (copying the token from a cookie to a header on each XHR request) and for regular non-ajax forms, be sure to use only POST and a hidden field as is normally done in CSRF protection of static server forms.

Is it safe to disable CSRF-protection for an authenticated POST endpoint in Rails?

I'm finding myself in a situation where I could provide a much nicer user experience if I could disable CSRF token checking for an endpoint in my rails app.
The endpoint is a create action (routed to by POST /whatever), that's behind a devise :authenticate! filter.
Would I open myself up to any additional security risks by disabling the CSRF-protection for that specific endpoint, or can I safely rely on the authentication before_filter to stop the kind of malicious requests that the CSRF token protects against?
Following is a bit more detailed explanation as to why I want to do this if anyone is interested.
My use case is that I basically want to create something very similar to the Facebook likebutton, but this button (unlike the Facebook counterpart) is commonly going to occur multiple times on the same page.
The CSRF protection works fine except for the case where the user visits the page with empty cookies.
In this case rails generates a new session for each of the X number of requests since they are all cookie-less. And, of course, for each new session a new CSRF token is generated and returned in the response to the iframe.
Since the browser only keeps one cookie for the domain, any subsequent requests from each of the iframes will be mapped to the same session, and thus all of the CSRF tokens (except one) are invalid.
The mapping to a single session is nice since the user can be prompted to log in once, and then be mapped to the same log in for each of the subsequent buttons presses – without having to reload the page.
A compromise would be to respond with a 401 Unauthorized, but preserve the session of the rejected request (by overriding handle_unverified_request). This would trigger the sign in popup again, but this time an instant redirect occurs since the user is already signed in.
It would, of course, be best to avoid that flash of the sign in popup window, and thus I'd like to disable the CSRF protection all together for just the create action.
Authenticated requests are precisely what CSRF is about.
What CSRF means is that the attacker convinces the user's browser to make a request. For example you visit a page hosted by an attacker that has a form that looks like
<form action="http://www.yourapp.com/some_action">
#for parameters here
</action>
And some javascript on the page that auto submits the form. If the user is already logged in to your app, then this request will pass any cookie based authentication checks. However the attacker doesn't know the csrf token.
For an unauthenticated request, csrf serves no purpose - the attacker can just go ahead and make the request anyway - they don't need to hijack the victim's credentials.
So, short version: disabling csrf protection will leave you vulnerable to csrf style attacks.
What CSRF is really about is making sure the form contains a parameter that an attacker can't fake. The session is an easy place to store such a value but I imagine you could come up with alternatives. For example if the user can't control any of the parameters in the form, you could add another parameter which would be a signature of all the other parameters in the form (possibly with some sort of timestamp or nonce to prevent replay attacks). Upon receiving the request you can tell whether the request is from a form you generated by verifying the signature.
Be very careful about this sort of stuff as it is easy to get wrong (and even the big boys get it wrong sometimes.

Resources