CSRF using AJAX. Why is it possible? - ajax

Steps to reproduce:
Go to https://stackoverflow.com/
Open developer console in your web browser
Execute the following JS code:
fetch(`https://webhook.site/5a6f8001-3646-4b3e-bd76-a4562af5a9ef`, { credentials: 'include' })
Go to https://webhook.site/#!/5a6f8001-3646-4b3e-bd76-a4562af5a9ef
You should see the HTTP request on https://webhook.site
Why does the HTTP request reach the server?
Does this violate Same Origin Policy?

First of all, do not mix SOP, CORS and CSRF.
SOP (same-origin policy) checks are performed by the browser and help to protect resource in your origin that access from "other" origins. If they needed to be allowed permission to they could read your contents, you need to define rules to extend to restrictions called CORS.
CSRF (cross-site request forgery) is an attack that is helping to create or send a malicious invalidated request via the victim's cookie. Every request after reach to target origin with the victim's cookie that will be executed via that user's permission. So, The attacker does not read the response. Also, you cannot read responses because of Same Origin Policy rules unless they do not allow read to their content with CORS rules.

Related

Can I "trust" request origin parameter?

Let's assume my site is example.com. On my server I have script which must works only for white listed site. I have setuped this code which allow XHR requests only from my site.
header('Access-Control-Allow-Origin: https://www.example.com')
Now I'm wondering can someone change origin parameter and send fake AJAX requests from another sites ?
So is origin parameter trusted or there is a ways to "override" origin parameter example from script or browser configuration or from some third part service ?
CORS policies are enforced on the client side; i.e. by the browser.
You can trust that they will work to prevent CSRF for your regular visitors, but there's nothing preventing someone from manually sending requests to you as they wish.

JASIG CAS CORS what should happen when Origin == "null"?

I am using a CAS server to secure my Spring applications which includes REST and HttpInvoker APIs.
When an application redirects to a CAS server in a different domain we get CORS
'issues'. I have added the eBay Cors-Filter https://github.com/eBay/cors-filter to the applications and the CAS server.
However...
when the application redirects to the CAS server for authentication, the Origin header is changed to 'null'. This is due to a "privacy-sensitive" context noted in the CORS spec (page 14, section 7.3).
...and, now at last...the question!
If the server receives an Origin header of 'null' can it proceed as normal, just returning 'null' in the Access-Control-Allow-Origin header?
Does this break anything?
Is it unsafe?
Cheers
Allowing null origins is unsafe. This blog post does a pretty good job of breaking down why. The short version is, it enables a form of CSRF. An allowed origin of null means "any page that redirects to me" (or any code running from a file:/// URL, but that's another topic).
Let's say your app at app.example.com uses a service at service.example.com, protected with CAS on auth.example.com. Also, let's assume you set up service with Access-Control-Allow-Origin: null so that you can make a fetch from app that points to auth with a redirect to service. Got all that? Good, you make a fetch for auth.example.com/cas/login?service=service.example.com, login happens (session cookie set on auth.example.com), redirect happens (session cookie set on service.example.com), app gets data from service. Because of your Access-Control headers, the browser lets your app read the response.
Now, imagine that you visit a malicious page (evil.com/hello) that knows about your service. They run a 302 page on evil.com/redir that points to service.example.com. Now, this malicious code can fetch evil.com/redir with credentials="include". The browser will request the redirect page, getting a 302 with ACA-Origin=https://evil.com and ACA-Credentials=true, Location of service.example.com. The browser follows the 302, requesting service.example.com and including the relevant session cookie. In this request, Origin is set to null but you've whitelisted that value, so your service sends an ACA-Origin value of null and the browser lets the (malicious) requesting code see the response. You just leaked authenticated data from your service to the injected script, which can send it home to the attacker.
Yes, I believe you can return null or the wildcard * to allow any origin.
Does this break anything?
If you're only returning null when Origin: null is received then it shouldn't affect anything else.
Is it unsafe?
I'm not familiar with CAS, however as long as you aren't sending Access-Control-Allow-Credentials too, and your CAS isn't restricted by IP or on the local network only, then this doesn't open up your system any more than anonymous access does. See this answer for technical details.
If you are, then setting Access-Control-Allow-Origin will allow other domains and origins to read data from CORS that the user visits, using their cookies for CAS.

How to handle CSRF in preflighted CORS POST request in django?

I am trying to make POST request via AJAX from abc.com to URL from xyz.com (which is a Django application).
I am getting CSRF token by making a GET request to a URL on xyz.com, but the token changes when an OPTIONS request is made to xyz.com in the preflighted request.
Is there any way to get the response of OPTIONS request in the preflighted request ?
Note:
I am following instructions from following sources :
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
http://www.html5rocks.com/en/tutorials/cors/
Django CSRF protection will allow OPTIONS requests, so no problem with the first stage:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-it-works
If I understand correctly, you then want the next request (e.g. a cross-domain POST) to be allowed through. For this to work and get past Django's CSRF protection, the request must send a CSRF token (in POST data or in header for AJAX) and a matching CSRF cookie.
Now, cross-domain restrictions make it impossible for abc.com to set or read a cookie for xyz.com, whether from javascript or from a server side response. Therefore, this approach is impossible.
Instead you will have to apply #csrf_exempt to the view. This would allow any site to post to it. Therefore, you'll need to build in some other protection to the view. You are, of course, on your own in checking the security of your protection. Remember that 'Referer' and 'Origin' headers can easily be forged with something as basic as curl.
See django-cors-headers, you may find it how it works more suitable to solve your problem:
https://github.com/ottoyiu/django-cors-headers/
Django-rest-framework recommends http://www.django-rest-framework.org/topics/ajax-csrf-cors

Same origin Policy and CORS (Cross-origin resource sharing)

I was trying to understand CORS. As per my understanding, it is a security mechanism implemented in browsers to avoid any AJAX request to domain other than the one open by the user (specified in the URL).
Now, due to this limitation many CORS was implemented to enable websites to do cross origin request. but as per my understanding implementing CORS defy the security purpose of the "Same Origin Policy" (SOP).
CORS is just to provide extra control over which request server wants to serve. Maybe it can avoid spammers.
From Wikipedia:
To initiate a cross-origin request, a browser sends the request with
an Origin HTTP header. The value of this header is the site that
served the page. For example, suppose a page on
http://www.social-network.example attempts to access a user's data
in online-personal-calendar.example. If the user's browser implements
CORS, the following request header would be sent:
Origin: http://www.social-network.example
If online-personal-calendar.example allows the request, it sends an
Access-Control-Allow-Origin header in its response. The value of the
header indicates what origin sites are allowed. For example, a
response to the previous request would contain the following:
Access-Control-Allow-Origin: http://www.social-network.example
If the server does not allow the cross-origin request, the browser
will deliver an error to social-network.example page instead of
the online-personal-calendar.example response.
To allow access to all pages, a server can send the following response
header:
Access-Control-Allow-Origin: *
However, this might not be appropriate for situations in which
security is a concern.
What am I missing here? what is the the intend of CORS to secure the server vs secure the client.
Same-origin policy
What is it?
The same-origin policy is a security measure standardized among browsers. The "origin" mostly refers to a "domain". It prevents different origins from interacting with each other, to prevent attacks such as Cross Site Request Forgery.
How does a CSRF attack work?
Browsers allow websites to store information on a client's computer, in the form of cookies. These cookies have some information attached to them, like the name of the cookie, when it was created, when it will expire, who set the cookie etc. A cookie looks something like this:
Cookie: cookiename=chocolate; Domain=.bakery.example; Path=/ [// ;otherDdata]
So this is a chocolate cookie, which should be accessible from http://bakery.example and all of its subdomains.
This cookie might contain some sensitive data. In this case, that data is... chocolate. Highly sensitive, as you can see.
So the browser stores this cookie. And whenever the user makes a request to a domain on which this cookie is accessible, the cookie would be sent to the server for that domain. Happy server.
This is a good thing. Super cool way for the server to store and retrieve information on and from the client-side.
But the problem is that this allows http://malicious-site.example to send those cookies to http://bakery.example, without the user knowing! For example, consider the following scenario:
# malicious-site.example/attackpage
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://bakery.example/order/new?deliveryAddress="address of malicious user"');
xhr.send();
If you visit the malicious site, and the above code executes, and same-origin policy was not there, the malicious user would place an order on behalf of you, and get the order at his place... and you might not like this.
This happened because your browser sent your chocolate cookie to http://bakery.example, which made http://bakery.example think that you are making the request for the new order, knowingly. But you aren't.
This is, in plain words, a CSRF attack. A forged request was made across sites. "Cross Site Request Forgery". And it would not work, thanks to the same-origin policy.
How does Same-origin policy solve this?
It stops the malicious-site.example from making requests to other domains. Simple.
In other words, the browser would not allow any site to make a request to any other site. It would prevent different origins from interacting with each other through such requests, like AJAX.
However, resource loading from other hosts like images, scripts, stylesheets, iframes, form submissions etc. are not subject to this limitation. We need another wall to protect our bakery from malicious site, by using CSRF Tokens.
CSRF Tokens
As stated, malicious site can still do something like this without violating the same-origin policy:
<img src='http://bakery.example/order/new?deliveryAddress="address of malicious user"'/>
And the browser will try to load an image from that URL, resulting in a GET request to that URL sending all the cookies. To stop this from happening, we need some server side protection.
Basically, we attach a random, unique token of suitable entropy to the user's session, store it on the server, and also send it to the client with the form. When the form is submitted, client sends that token along with the request, and server verifies if that token is valid or not.
Now that we have done this, and malicious website sends the request again, it will always fail since there is no feasible way for the malicious website to know the token for user's session.
CORS
When required, the policy can be circumvented, when cross site requests are required. This is known as CORS. Cross Origin Resource Sharing.
This works by having the "domains" tell the browser to chill, and allow such requests. This "telling" thing can be done by passing a header. Something like:
Access-Control-Allow-Origin: //comma separated allowed origins list, or just *
So if http://bakery.example passes this header to the browser, and the page creating the request to http://bakery.example is present in the origin list, then the browser will let the request go, along with the cookies.
There are rules according to which the origin is defined1. For example, different ports for the same domain are not the same origin. So the browser might decline this request if the ports are different. As always, our dear Internet Explorer is the exception to this. IE treats all ports the same way. This is non-standard and no other browser behaves this way. Do not rely on this.
JSONP
JSON with Padding is just a way to circumvent same-origin policy, when CORS is not an option. This is risky and a bad practice. Avoid using this.
What this technique involves is making a request to the other server like following:
<script src="http://badbakery.example/jsonpurl?callback=cake"></script>
Since same-origin policy does not prevent this2 request, the response of this request will be loaded into the page.
This URL would most probably respond with JSON content. But just including that JSON content on the page is not gonna help. It would result in an error, ofcourse. So http://badbakery.example accepts a callback parameter, and modifies the JSON data, sending it wrapped in whatever is passed to the callback parameter.
So instead of returning,
{ user: "vuln", acc: "B4D455" }
which is invalid JavaScript throwing an error, it would return,
cake({user: "vuln", acc:"B4D455"});
which is valid JavaScript, it would get executed, and probably get stored somewhere according to the cake function, so that the rest of the JavaScript on the page can use the data.
This is mostly used by APIs to send data to other domains. Again, this is a bad practice, can be risky, and should be strictly avoided.
Why is JSONP bad?
First of all, it is very much limited. You can't handle any errors if the request fails (at-least not in a sane way). You can't retry the request, etc.
It also requires you to have a cake function in the global scope which is not very good. May the cooks save you if you need to execute multiple JSONP requests with different callbacks. This is solved by temporary functions by various libraries but is still a hackish way of doing something hackish.
Finally, you are inserting random JavaScript code in the DOM. If you aren't 100% sure that the remote service will return safe cakes, you can't rely on this.
References
1. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Definition_of_an_origin
2. https://www.w3.org/Security/wiki/Same_Origin_Policy#Details
Other worthy reads
http://scarybeastsecurity.blogspot.dk/2009/12/generic-cross-browser-cross-domain.html
https://www.rfc-editor.org/rfc/rfc3986 (sorry :p)
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
The Same Origin Policy (SOP) is the policy browsers implement to prevent vulnerabilities via Cross Site Scripting (XSS). This is mainly for protecting the server, as there are many occasions when a server can be dealing with authentication, cookies, sessions, etc.
The Cross Origin Resource Sharing (CORS) is one of the few techniques for relaxing the SOP. Because SOP is "on" by default, setting CORS at the server-side will allow a request to be sent to the server via an XMLHttpRequest even if the request was sent from a different domain. This becomes useful if your server was intended to serve requests from other domains (e.g. if you are providing an API).
I hope this clears up the distinction between SOP and CORS and the purposes of each.

XHR2 allow origin - can this be faked?

With regards to the way resources accessed over XHR2/CORS can block the request unless it came from a whitelisted domain:
which header is read to determine the referrer domain - is it the standard HTTP_REFERRER?
could someone send a request pretending to be from another domain somehow?
I'm aware CORS is not a reliable means of securing data - I ask only as a point of curiosity.
The header that is read is Origin. As any HTTP header it can be faked. The idea behind COSR is to enable sending data, while still securing the user / preventing abusing of user session. The cross-domain requests are forbidden to protect the user, not the server.
The attacker should both send the request pretending this is another domain AND send the cookies the user has. And this is not something you can achieve via XSS alone - you have to steal the cookie and send the request on your own. But you cannot steal the cookie for site A from site B. If A however accepts request from any domain, via XSS on B you can trick the user's browser to send request to A, the browser will send the cookies and you can read the response back.
The Origin header contains the requesting domain.
This browser is in complete control of this header, and it cannot be faked. The browser controls this header on behalf of the user, and user's cannot override the value in the JS code.
Note that I said "the browser"; as with any HTTP request, the user could craft a curl request with any Origin header. But this has limited use as an attack vector, since the hacker would have to trick a valid user into issuing the correct curl request, which is unlikely.

Resources