I am soliciting advice on correctly implementing nonces for ajax powered forms.
The forms are written in plain html.
The way, I have currently implemented is - the default form submit is intercepted through javascript and before processing the form, a get call is made to get a nonce token in JSON, the resulting nonce data is appended to the form data and is sent to the server for further processing via POST (XHR).
For security reasons, I have limited the scope of the nonce to 300 seconds. And the url that generates nonce values check for http referer to ensure that requests are made from the site. However, this may be futile due to the reasons mentioned in Referer header: privacy and security concerns (MDN)
Thank you.
Related
I am newbie to website security and currently trying to understand Same-Origin-Policy in some depth.
While there are very good posts on stackoverflow and elsewhere about the concept of SOP, I could not find updated information on whether chrome and other browsers allow cross-domain XHR post requests to be 'sent' from the first place.
From this 5 year old post, it appears that chrome allows the request to pass through to the requested server but does not allow reading the response by the requester.
I tested that on my website trying to change user info on my server from a different domain. Details below:
My domain: "www.mysite.com"
Attacker domain: "www.attacker.mysite.com"
According to Same-Origin-Policy those two are considered different Origins.
User (while logged in to www.mysite.com) opens www.attacker.mysite.com and presses a button that fires a POST request to 'www.mysite.com' server...The submitted hidden form (without tokens in this case) has all the required information to change the user's info on 'www.mysite.com' server --> Result: CSRF successful attack: The user info does indeed change.
Now do the same but with javascript submitting the form through JQuery .post instead of submitting the form--> Result: Besides chrome giving the normal response:
No 'Access-Control-Allow-Origin' header is present on the requested
resource
, I found that no change is done on the server side...It seems that the request does not even pass through from the browser. The user info does not change at all! While that sounds good, I was expecting the opposite.
According to my understanding and the post linked above, for cross-domain requests, only the server response should be blocked by the browser not sending the post request to the server from the first place.
Also, I do not have any CORS configuration set; no Access-Control-Allow-Origin headers are sent. But even if I had that set, that should apply only on 'reading' the server response not actually sending the request...right?
I thought of preflights, where a request is sent to check if it's allowed on the server or not, and thus blocking the request before sending its actual data to change the user info. However, according to Access_Control_CORS , those preflights are only sent in specific situations which do not apply to my simple AJAX post request (which includes a simple form with enctype by default application/x-www-form-urlencoded and no custom headers are sent).
So is it that chrome has changed its security specs to prevent the post request to a cross domain from the first place?
or am I missing something here in my understanding to the same-origin-policy?
Either way, it would be helpful to know if there is a source for updated security measures implemented in different web browsers.
The XMLHttpRequest object behavior has been revisited with time.
The first AJAX request were unconstrained.
When SOP was introduced the XMLHttpRequest was updated to restrict every cross-origin request
If the origin of url is not same origin with the XMLHttpRequest origin the user agent should raise a SECURITY_ERR exception and terminate these steps.
From XMLHttpRequest Level 1, open method
The idea was that an AJAX request that couldn't read the response was useless and probably malicious, so they were forbidden.
So in general a cross-origin AJAX call would never make it to the server.
This API is now called XMLHttpRequest Level 1.
It turned out that SOP was in general too strict, before CORS was developed, Microsoft started to supply (and tried to standardize) a new XMLHttpRequest2 API that would allow only some specific requests, stripped by any cookie and most headers.
The standardization failed and was merged back into the XMLHttpRequest API after the advent of CORS. The behavior of Microsoft API was mostly retained but more complex (read: potentially dangerous) requests were allowed upon specific allowance from the server (through the use of pre-flights).
A POST request with non simple headers or Content-Type is considered complex, so it requires a pre-flight.
Pre-flights are done with the OPTIONS method and doesn't contain any form information, as such no updates on the server are done.
When the pre-flight fails, the user-agent (the browser) terminate the AJAX request, preserving the XMLHttpRequest Level 1 behavior.
So in short: For XMLHttpRequest the SOP was stronger, deny any cross-origin operations despite the goals stated by the SOP principles. This was possible because at the time that didn't break anything.
CORS loosened the policy allowing "non harmful" requests by default and allowing the negotiation of the others.
OK...I got it...It's neither a new policy in chrome nor missing something in SOP...
The session cookies of "www.mysite.com" were set to "HttpOnly" which means, as mentioned here, that they won't be sent along with AJAX requests, and thus the server won't change the user's details in point (4).
Once I added xhrFields: { withCredentials:true } to my post request, I was able to change the user's information in a cross-domain XHR POST call as expected.
Although this proves the already known fact that the browser actually sends the cross-domain post requests to the server and only blocks the server response, it might still be helpful to those trying to deepen their understanding to SOP and/or playing with CORS.
I know all about SQL injections, and peeking into javascript files that a website uses, and also that GET requests contain all of the information in a URL.
Is there any security concern that is special to AJAX and only pertains to using AJAX?
For example, sending post requests via AJAX seems completely safe to me. Barring SQL injections, I can't think of one thing that could go wrong... is this the correct case?
Also, are "requests" of any kind that a user's browser sends or any information it receives available to be viewed by a third party who should not be viewing? And can that happen to AJAX post requests ('post' requests specifically; not 'get')?
It's like any other form of data input: validate your values, check the referrer, authenticate the session, use SSL.
Do I need to use csrf tokens in my ajax requests?
I think that someone tricking my users to execute malicious ajax requests from another site, to my site, will fail because of the origin policy, which is handled by the browser, am I right?
I don't care about duplicated requests when using ajax, I'm only asking about the attacks.
Am I at risk if I don't use csrf in my ajax requests?
As per my research,
We can reduce the vulnerability by using POST request(for the request which will take side affect). But thing is that we can forge the POST request as well with form submission, cause same origin policy will not applies plain html for submissions. But it applies to request which are getting generated with JS.
So if you use ajax POST request and you are safe if you are using JSON payload.
Cause url encoded payload(POST request) can be forged with form submission from other sites.
If you use JSON, as it is not possible to send plain text pay load(with out urlform encoded) with html form submission you are safe. Because with ajax POST request if you use urlform encoded data payload it can be forged with POST form submission.
This is how we solved the problem with CSRF token in Ajax requests
http://mylifewithjava.blogspot.com/2010/11/implicit-csrf-protection-of-ajax_22.html
The fact that you are using Ajax doesn't mean that others have to as well. Your server won't be able to distinguish a request made by XHR from one made by <form> submission. (Yes XHR usually adds a header identifying itself, but this is not hard to spoof.)
So yes, you do need to consider CSRF attacks.
Edit
Django have a POC, which is why they and Ruby on Rails now implement CSRF protection on AJAX requests.
Once again, please check your facts before downvoting, and explain what the downvote is for.
When user is on the page you can use session or cookies to check who is he.
But when AJAX is used, for example, for sending an answer, sending page have no contact with user. How can it check is it real registered user, or just spambot sending this by headers?
What is the common practice for AJAX user validation?
AJAX requests contain the same cookies like regular requests. Besides that you can send any arguments like session IDs with the AJAX request.
Actually, for the server it makes absolutely no difference if a request is made through an XmlHttpRequest object or not. Most frameworks add an X-Requested-With: XMLHttpRequest header though but that's completely optional.
So.. whatever means you use to pass your session data, simply ensure it's also available to the script called with your AJAX request:
If you have a session id passed via GET/POST, include it in your request's arguments.
If cookies are needed, ensure they are send to the file. If it's in the same folder like the current file or a descendant of it you are usually safe. If it's on another (sub-)domain you might get problems - not only with cookies but alsowith cross-domain AJAX which usually isn't allowed due to the same-origin policy browsers have.
Django comes with CSRF protection middleware, which generates a unique per-session token for use in forms. It scans all incoming POST requests for the correct token, and rejects the request if the token is missing or invalid.
I'd like to use AJAX for some POST requests, but said requests don't have the CSRF token availabnle. The pages have no <form> elements to hook into and I'd rather not muddy up the markup inserting the token as a hidden value. I figure a good way to do this is to expose a vew like /get-csrf-token/ to return the user's token, relying on browser's cross-site scripting rules to prevent hostile sites from requesting it.
Is this a good idea? Are there better ways to protect against CSRF attacks while still allowing AJAX requests?
UPDATE: The below was true, and should be true if all browsers and plugins were properly implemented. Unfortunately, we now know that they aren't, and that certain combinations of browser plugins and redirects can allow an attacker to provide arbitrary headers on a cross-domain request. Unfortunately, this means that even AJAX requests with the "X-Requested-With: XMLHttpRequest" header must now be CSRF-protected. As a result, Django no longer exempts Ajax requests from CSRF protection.
Original Answer
It's worth mentioning that protecting AJAX requests from CSRF is unnecessary, since browsers do not allow cross-site AJAX requests. In fact, the Django CSRF middleware now automatically exempts AJAX requests from CSRF token scanning.
This is only valid if you are actually checking the X-Requested-With header server-side for the "XMLHttpRequest" value (which Django does), and only exempting real AJAX requests from CSRF scanning.
If you know you're going to need the CSRF token for AJAX requests, you can always embed it in the HTML somewhere; then you can find it through Javascript by traversing the DOM. This way, you'll still have access to the token, but you're not exposing it via an API.
To put it another way: do it through Django's templates -- not through the URL dispatcher. It's much more secure this way.
Cancel that, I was wrong. (See comments.) You can prevent the exploit by ensuring your JSON follows the spec: Always make sure you return an object literal as the top-level object. (I can't guarantee there won't be further exploits. Imagine a browser providing access to the failed code in its window.onerror events!)
You can't rely on cross-site-scripting rules to keep AJAX responses private. For example, if you return the CSRF token as JSON, a malicious site could redefine the String or Array constructor and request the resource.
bigmattyh is correct: You need to embed the token somewhere in the markup. Alternatively, you could reject any POSTs that do have a referer that doesn't match. That way, only people with overzealous software firewalls will be vulnerable to CSRF.