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.
Related
I have a fully working product on Rails 5. I now wish to make a Chrome extension, using which users can create an 'Article'.
However, requests from my Chrome extension will be treated as Cross Site by my rails app. Hence, I was thinking of not doing the CSRF check at all on just my create action.
What is the biggest security risk associated with this? I understand after this, anyone will be able to make POST request to my server that creates a new article - however, this is not a damaging action like update, or worse, delete.
The Rails guide states that,
CSRF attack method works by including malicious code or a link in a
page that accesses a web application that the user is believed to have
authenticated. If the session for that web application has not timed
out, an attacker may execute unauthorized commands.
If a CSRF token is a valid one, it is a kind of assurance that the user session has not been hijacked and the request has been made with the user consent.
For more info, I recommend you to refer the Rails guide http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
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
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.
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.
Our rails3 application talks to another rails application exposed as REST functions. We get the following warning post migration to rails3 on all POST calls made to the REST services.
WARNING: Can't verify CSRF token authenticity
How should we pass the csrf token on the wire when we make a POST call to the REST services, the ApplicationController has protect_from_forgery method and the call also lands on the handle_unverified_request call. We use HTTP basic auth to authenticate and it seems to work fine. What should we do to resolve this issue.
Ok now I'll give it a shot to answer your question.
CSRF tokens are "session dependent" this means the user must share a session with the application he is communicating with. This means a request must have been made before he submits the actual form which is the case for standard HTML forms where the form is displayed before it is submitted so there's room to generate a CSRF token for this user.
Let's call the Application which serves the UI Button App1 and the one hosting the REST Service App2.
The User shares a session with App1 and gets the UI Button served by App1. Once the user clicks the button a request to App1 is made, and App1 makes a request to the REST Service of App2.
Conclusion: The user doesn't share a session with App2 but your App1 has a session with App2. This concludes in two things:
The User is not vulnerable to Cross-Site-Forgery on App2 because he doesn't have a session on that server. So if I post a something like <img src="http://app2.com/rest-service/destroy> here, App2 wouldn't recognise me because I don't share a session with App2 and nothing happens.
You can implement your own security measures on the REST Service to secure it from the public:
Authentication by HTTP Basic Auth
Authentication by an API Key
....
This means you can drop the CSRF protection on the REST Service and stay on the safe side as long as user's can't make direct calls via AJAX etc.
Addition
CSRF protects your site from submitted forms that do not come from your origin (actually this applies to POSTed forms only since they usually manipulate data.
The big scenario is: I'm the attacker and I'd like to update your users account name to "Mr. Potatoe" the way I'd do that is to place a hidden form on my website which gets POSTed to Yourdomain.com/account with a hidden field like account[name]="Mr. Potatoe". Now what happens without CSRF protection? My browser submits the form, and sends the authentication cookie with it and my name is now "Mr. Potatoe". What happens with CSRF protection? My browsers submits the form and sends the cookie but the form has no CSRF token so the request gets rejected. Now is there a way as attacker to get the CSRF token under these circumstances? The answer is no. Maybe you ask yourself....
What if I place a hidden iframe on my site which points to Yourdomain.com/account/edit and just copy the hidden field containing the token? - Answer: It won't work because of same origin policy, you can't read whats inside an iframe if its content doesn't come from your domain.
What if I make an AJAX call in the background to get the token? Answer: It won't work because using pure AJAX you are bound to the same origin policy as well.
Now let's get to the point: I can't send a hidden AJAX call from my page to yours to harm your user who is on my site.
What does it mean? You can implement a before filter which checks if request.xhr? is true or whether the user agent is something like "My REST Client XY" because this can't be forged by a cross site request in a >>browser<<. So if this is the case you can ignore/disable the CSRF protection.
By the way if you want to make an AJAX request within your site you can get the CSRF token like this:
var token = $('meta[name="csrf-token"]').attr('content');
See the Rails UJS Script: https://github.com/rails/jquery-ujs/blob/master/src/rails.js#L81
NOTE: this only protects you from cross site forgery and nothing else....