I'm studing HTML5's security problems. I saw all the presentations made by Shreeraj Shah. I tried to simulate a basic CSRF attack with my own servers using withCredentials tag sets to true (so in the response message the cookies should be replayed) and adding Content-Type sets to text/plain in the request (to bypass the preflight call).
When I tried to start the attack the browser told me that the XMLHttpRequest can not be accomplish because of the Access-Control-Allow-Origin header. So I put a * in the header of the victim's web page and the browser told me that I can't use the * character when I send a request with withCredentials sets to true.
I tried to make the same thing with the web apps stored in the same domain, and all was fine (I suppose it is because the browser doesn't check if the request comes from the same domain).
I'm asking, it's a new features that modern browsers set up recently to avoid this kind of problems?
Because in the Shreeraj's videos, the request was across different domains and it worked...
Thank you all and sorry for my english :-)
EDIT:
I think I found the reason why the CSRF attack doesn't work fine as in the Shreeraj's presentations.
I read the previous CORS document, published in 2010, and I found that there wasn't any recommendation about the with credential flag setted to true when Access-Control-Allow-Origin is set to *, but if we look at the last two publications about CORS (2012 and 2013), in the section 6.1, one of the notes is that we can't make a request using with credentials flag setted to true if the Access-Control-Allow-Origin is set to *.
Here are the links:
The previous one (2010): http://www.w3.org/TR/2010/WD-cors-20100727/
The last two (2012, 2013): http://www.w3.org/TR/2012/WD-cors-20120403/ --- http://www.w3.org/TR/cors/
Here is the section I'm talking about: http://www.w3.org/TR/cors/#supports-credentials
If we look at the previous document we can not find it, because there isn't.
I think this is the reason why the simple CSRF attack made in 2012 by Shreeraj Shah today doesn't work (of course in modern browsers that follow the w3c's recommendations). Could it be?
The request will still be made despite the browser error (if there's no pre-flight).
The Access-Control-Allow-Origin simply allows access to the response from a different domain, it does not affect the actual HTTP request.
e.g. it would still be possible for evil.com to make a POST request to example.com/transferMoney even though there are no CORS headers set by example.com using AJAX.
Related
I added a preload for my site like this <link rel=preload href=http://api.github.com/... as=fetch crossorigin=anonymous> which is fetch()ed later. It worked very well (the preload request sent to the remote server at the very beginning of loading, and answer came back, everything was fine).
Later I added an Authorization: Bearer ... to the fetch() call (because of other reasons), which is caused that the preload's HTTP headers do not match the later fetch's HTTP headers, so the entire preload result is not re-used anymore (Both Chrome and Firefox are correctly notify me about this).
I also tried to add the preload with Link HTTP header to the main page's response, but that did not help as well.
So the current sitation is this: I can't add the same Authorization header to preload request because it is simply not possible, so the two request are never will be the same, so the preload is useless anymore.
Please correct me and advise:
Is there a way to add that Authorization: Bearer ... to the preload request to?
OR is there a way to ask the browser to ignore that difference between to two request's headers?
OR any other idea?
Web standards do not allow for such a possibility.
The Fetch standard defines the conditions under which prefetched resources may be used as follows:
To fetch, given a request request, […] run the steps below. […]
If all of the following conditions are true:
[…]
request’s unsafe-request flag is not set or request’s header list is empty
then:
Let foundPreloadedResource be the result of invoking consume a preloaded resource for [request]
Having an Authorization header in the fetch request disqualifies it from reusing preloaded resources. Unless you happen to know of a non-standard extension that allows you to bypass this, this means there is no way to prefetch a resource with custom headers.
There are three ways you can resolve this: skip the Authorization header in the request proper, give up on preloading entirely, or reimplement prefetching yourself. That is, inject a script that fetches the resource early during page loading, preferably gated by network.connection && !network.connection.saveData, and stores it in your own cache, then simply look up the data there.
The order I listed those solutions is one of, in my opinion, decreasing appropriateness. Prefetching has been designed mostly for the sake of static resources that present the same to any user that may want to download them; as such, an Authorization header is not supposed to matter, so if you can get away with avoiding it, do. If authorization does matter, then maybe the resource isn’t such a great candidate for prefetching. If you insist though, you can do it manually.
Chrome has a long history of ignoring Set-Cookie header. Some of these reasons have been termed bugs and fixed, others are persistent. None of them are easy to find in documentation.
Set-Cookie not allowed in 302 redirects
Set-Cookie not allowed if host is localhost
Set-Cookie not allowed if Expires is out of acceptable range
I am currently struggling with getting chrome to accept a simple session cookie. Firefox and Safari seem to accept most any RFC compliant string for Set-Cookie. Chrome stubbornly refuses to acknowledge that a Set-Cookie directive was even sent on the request (does not show up in Developer Tools (Network)). curl looks fine.
So does anyone have either 1) modern best practices for cross-browser Set-Cookie formatting or 2) more information regarding what can cause Chrome to bork here?
Thanks.
One thing that has bitten me and is not on your list: if you are trying to set a secure cookie through HTTP on localhost, Chrome will reject it because you are not using HTTPS.
This kind of makes sense, but is annoying for local development. (Firefox apparently makes an exception for this case and allow to set secure cookies over HTTP on localhost).
I work in a corporate environment and found an issue I can't resolve.
It has to do with EventSource changing the URL param from HTTP to HTTPS.
const url = 'http://localhost:8080'; // <-- using HTTP not HTTPS
new window.EventSource(url);
Which results in the browser throwing this error:
GET https://localhost:8080 net::ERR_TUNNEL_CONNECTION_FAILED
I am developing on a website using HTTPS so maybe this is by design that it uses the same protocol. Anyone experienced this issue or know how to resolve it?
--- Update ---
Looks like it is by design. When attempting this on another HTTPS site I got this:
Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure EventSource endpoint 'http://localhost...'. This request has been blocked; the content must be served over HTTPS.
The question still remains, how do I get around this?
Eventsource won't change between http and https. Are you using the HTTPS Everywhere plugin for Chrome, or something like that?
I think you are being hit by the same-origin policy. What this means is that the SSE connection must be to the same origin, which basically means the same hostname and domain, same scheme (i.e. either both http or both https) and same port.
You can use CORS to get around this. At the top of your SSE script you need to send back this header:
Access-Control-Allow-Origin: *
This says anyone, from anywhere, is allowed to connect and get the data stream. It has to be done on the server script, there is no way to do it from the client. (By design: the whole point of same-origin policy is to stop people using other people's content and making it look like their own, without permission.)
Shameless Plug: see my chapter 9 in my book (Data Push Apps with HTML5 SSE, O'Reilly) for finer control of allow-origin, and how it interacts with cookies and basic auth.
BTW, I notice I mention that Chrome won't work with self-signed https certificates. To be honest I'm not sure if that is still the case, but that might also be something to watch out for when using https and localhost.
I made a POST request to a HTTP (non-HTTPS) site, inspected the request in Chrome's Developer Tools, and found that it added its own header before sending it to the server:
Upgrade-Insecure-Requests: 1
After doing a search on Upgrade-Insecure-Requests, I can only find information about the server sending this header:
Content-Security-Policy: upgrade-insecure-requests
This seems related, but still very different since in my case, the CLIENT is sending the header in the Request, whereas all the information I've found is concerning the SERVER sending the related header in a Response.
So why is Chrome (44.0.2403.130 m) adding Upgrade-Insecure-Requests to my request and what does it do?
Update 2016-08-24:
This header has since been added as a W3C Candidate Recommendation and is now officially recognized.
For those who just came across this question and are confused, the excellent answer by Simon East explains it well.
The Upgrade-Insecure-Requests: 1 header used to be HTTPS: 1 in the previous W3C Working Draft and was renamed quietly by Chrome before the change became officially accepted.
(This question was asked during this transition when there were no official documentation on this header and Chrome was the only browser that sent this header.)
Short answer: it's closely related to the Content-Security-Policy: upgrade-insecure-requests response header, indicating that the browser supports it (and in fact prefers it).
It took me 30mins of Googling, but I finally found it buried in the W3 spec.
The confusion comes because the header in the spec was HTTPS: 1, and this is how Chromium implemented it, but after this broke lots of websites that were poorly coded (particularly WordPress and WooCommerce) the Chromium team apologized:
"I apologize for the breakage; I apparently underestimated the impact based on the feedback during dev and beta."
— Mike West, in Chrome Issue 501842
Their fix was to rename it to Upgrade-Insecure-Requests: 1, and the spec has since been updated to match.
Anyway, here is the explanation from the W3 spec (as it appeared at the time)...
The HTTPS HTTP request header field sends a signal to the server expressing the client’s preference for an encrypted and authenticated response, and that it can successfully handle the upgrade-insecure-requests directive in order to make that preference as seamless as possible to provide.
...
When a server encounters this preference in an HTTP request’s headers, it SHOULD redirect the user to a potentially secure representation of the resource being requested.
When a server encounters this preference in an HTTPS request’s headers, it SHOULD include a Strict-Transport-Security header in the response if the request’s host is HSTS-safe or conditionally HSTS-safe [RFC6797].
This explains the whole thing:
The HTTP Content-Security-Policy (CSP) upgrade-insecure-requests
directive instructs user agents to treat all of a site's insecure URLs
(those served over HTTP) as though they have been replaced with secure
URLs (those served over HTTPS). This directive is intended for web
sites with large numbers of insecure legacy URLs that need to be
rewritten.
The upgrade-insecure-requests directive is evaluated before
block-all-mixed-content and if it is set, the latter is effectively a
no-op. It is recommended to set one directive or the other, but not
both.
The upgrade-insecure-requests directive will not ensure that users
visiting your site via links on third-party sites will be upgraded to
HTTPS for the top-level navigation and thus does not replace the
Strict-Transport-Security (HSTS) header, which should still be set
with an appropriate max-age to ensure that users are not subject to
SSL stripping attacks.
Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests
Quite simple really:
var req:URLRequest=new URLRequest();
req.url="http://somesite.com";
var header:URLRequestHeader=new URLRequestHeader("my-bespoke-header","1");
req.requestHeaders.push(header);
req.method=URLRequestMethod.GET;
stream.load(req);
Yet, if I inspect the traffic with WireShark, the my-bespoke-header is not being sent. If I change to URLRequestMethod.POST and append some data to req.data, then the header is sent, but the receiving application requires a GET not a POST.
The documentation mentions a blacklist of headers that will not get sent. my-bespoke-header is not one of these. It's possibly worth mentioning that the originating request is from a different port on the same domain. Nothing is reported in the policyfile log, so it seems unlikely, but is this something that can be remedied by force loading a crossdomain.xml with a allow-http-request-headers-from despite the fact that this is not a crossdomain issue? Or is it simply an undocumented feature of the Flash Player that it can only send custom headers with a POST request?
From what I can gather, it seems like your assumption about the lack of custom headers support for HTTP GET is indeed an undocumented feature (or a bug?) in the standard libraries.
In any case, you might want to see if as3httpclient would fit your purposes and let you work around this issue. Here's a relevant snippet from a post in the blog of the developer of this library:
"I was not able to set the header of a
HTTP/GET request. Macromedia Flash
Player allows you set the header only
for POST requests. I discussed this
issues with Ted Patrick and he told me
how I can us Socket to achieve the
desired and he was very kind to give a
me code-snippet, which got me
started."
If this limitation was undocumented at one time, that's no longer the case. See:
http://livedocs.adobe.com/flex/3/langref/flash/net/URLRequest.html#requestHeaders
"[...] Due to browser limitations, custom HTTP request headers are only supported for POST requests, not for GET requests. [...]"