Issue 436451: Remote websites can know which HSTS enabled websites the users have visited
Reported by
imfaster...@gmail.com,
Nov 25 2014
|
||||||||||||
Issue descriptionVULNERABILITY DETAILS If we ask Chrome to load http://example.com:443, it will definitely fail, because Chrome will make plain-text HTTP request to port 443 of the server. However, if example.com is a Known HSTS Host of Chrome (meaning either the user has visited https://example.com before, or it is on the HSTS preload list), it will send request to https://example.com:443, and the request will succeed. We can use JavaScript to differentiate the two cases, since in the first case, onerror event is triggered, while in the second case, onload event is triggered. Therefore, a malicious website can include well-chosen cross-domain images and use this trick to brute-force a list of domains that users have visited. Note that the list could only contain HSTS-enabled but not preloaded websites. One possible fix I can think of is to always treat http://example.com:443 as https://example.com:443 regardless of whether HSTS is set or not, while other ports are unaffected. 443 is reserved for HTTPS anyway. VERSION Chrome Version: [39.0.2171.65] + [stable] Operating System: Windows 8.1 REPRODUCTION CASE Just use Chrome to open the attached file, HSTSpoc.html. Nov 25 2014,Thank you for your reply! I read issue 33445 , but I believe my report is quite different from that. What issue 33445 describes is that websites can use one subdomain of a HSTS enabled host to store 1 bit of data, so that HSTS can be used as cookie in order to track users. However this issue is about the leak of information. The major difference is that in issue 33445 , a website can only know if the users have visited his own website. What I propose is that a website can know if users have visited any specific HSTS-enabled non-preloaded websites, which is quite similar to the CSS :visited issue [1]. I'm not sure if this is inherent from the spec, but it seems to me that it's a vulnerability in Chrome's way of parsing the URL like "http://example:443". If Chrome always treats it as a https request (may be against some spec), then this issue is gone, because I can no longer use onerror, onload to differentiate http and https requests. [1] https://hacks.mozilla.org/2010/03/privacy-related-changes-coming-to-css-vistited/ Nov 25 2014,Of course, another way to fix is to always throw an error whenever the browser encounters a http link that explicitly specifies port 443. I see neither of two methods will cause compatibility issue, because it's really unlikely that some people use the well-known 443 port to do http connections. Nov 26 2014,
The problem is indeed more like the CSS :visited thing than the ever-cookie thing. Both of the proposed fixes seem potentially workable to me. lgarron, any interest in taking this one on? battre, cbentzel: Any opinions? Nov 26 2014,Yep, definitely looks like the CSS :visited problem (the proof of concept works for me). Clever. I care about HSTS and am about to start URL normalization work, so this definitely sounds relevant. Serving HTTP over port 443 is definitely possible right now (`sudo python -m SimpleHTTPServer 443` works in Chrome just like any other port). Are there any legitimate use cases for it? Testing? Workarounds for proxies? The HSTS spec already has special cases for ports 80 and 443: http://tools.ietf.org/html/rfc6797#section-8.3 As long as there is no other precedent/guidance, either fix sounds sensible. -------------------------------- As long as there are no/few legitimate use cases, I think blocking HTTP over 443 is cleaner: no new magical rewriting rules, just blocked requests (which are a common failure mode). How about this? - Block HTTP requests over port 443. - Log how many requests are blocked by the code (for opted-in UMA users). - Carefully watch the stats. Also announce the change and see if people report legitimate, unfixable breakage. - If there are problems, change to rewrite instead of blocking. -------------------------------- The proof of concept also works in Firefox, so it's not just Chrome. I suppose we don't have to implement the same fix if it's not in any spec, but is there a usual plan for coordination/timeline/disclosure? Nov 26 2014,Setting to medium severity, given that http://www.chromium.org/developers/severity-guidelines explicitly lists this as an example: "Bug that allows an attacker to enumerate recently visited URLs." Arguably, this doesn't affect all sites. But I presume it affects sites with a strong tendency to value user privacy, and we want HSTS to be a good value proposition for them (even if they're not on the preload list [yet] – and they wouldn't be able to get on the preload list before we can fix this, either way). Nov 26 2014,
Nov 26 2014,
Also changing to Priority 1, to match the severity guidelines. Nov 26 2014,Adding davidben@ for input from networking about the choices. (I also pinged rsleevi@ separately.) Here's a possible fix based on redirect suggestion: https://codereview.chromium.org/764473002/ Nov 26 2014,
+mkwst FYI. I think that the proposal in #5 sounds good. Please make sure to document in the code why we decided to blog HTTP over port 443. ;-) I wonder whether we should have a "This page has been blocked to protect your privacy and work around crbug.com/436451 . If you really need this, start Chrome with parameter --foobar". Nov 26 2014,I largely agree with #5 as well. But how about just only blocking cross-domain HTTP requests on port 443, while allowing base html pages and same-domain resources? Of course, totally blocking http on 443 seems fine for me as well. And yes, this issue exists in Firefox as well, and I have privately reported it to Mozilla. Nov 26 2014,I talked with rsleevi@, and realized we didn't articulate why this is different from fingerprinting. If you own 1.example.com, 2.example.com, etc, you can make calls to https://#.example.com/setHSTS that returns an HSTS header, and then query http://#.example.com/get to see if you get an HTTP or an HTTPS response (don't redirect, send different responses based on the scheme). However, this issue describes a way to determine if someone has visited a non-preloaded HSTS site: 1) trivially and reliably (just check an error code, no need to do something like timing, no need to rely on a rarer feature like CORS misconfiguration or CSP: https://crbug.com/313737 and https://crbug.com/366251 ), and 2) even (especially!) if the site in question is following best practices. So long as we haven't completely given up on the "I know what sites you visited last summer" battle, this issue sounds worth fixing to me. (Whereas the fingerprinting battle is explicitly lost: http://www.chromium.org/Home/chromium-security/security-faq#TOC-Why-isn-t-passive-browser-fingerprinting-including-passive-cookies-in-Chrome-s-threat-model- .) Blocking cross-domain HTTP requests over port 443 (at least for <img> sources, but might as well do it for anything) sounds like a reasonable compromise, but I don't know how easy that would be. Nov 27 2014,
Actually adding davidben@ now. Nov 29 2014,After reconsidering this issue, I changed my mind. Now I think it's better to completely block HTTP requests over 443. The reason is that RFC 3986 section 7.2 says: Applications should prevent dereference of a URI that specifies a TCP port number within the "well-known port" range (0 - 1023) unless the protocol being used to dereference that URI is compatible with the protocol expected on that well-known port. The spec explicitly requires UA to block non-compatible well-known ports. If some people really use port 443 for the purpose other than HTTPS, it's better to break them now, rather than causing problems in the future. [1] https://tools.ietf.org/html/rfc3986#section-7.2 Dec 1 2014,
Dec 2 2014,
Assigning to myself, since I've been working on it. (I'm currently in some pretty heavy debugging to figure out how to block port 443 properly for non-HTTPS protocols.) Dec 4 2014,
I'm making this bug public in preparation for announcing https://crrev.com/770343003 on a few mailing lists. This is certainly not urgent enough for a merge to stable, so the information would be out for a while, anyhow. There's also a public Bugzilla bug about this, so the technique is nominally public already: https://bugzilla.mozilla.org/show_bug.cgi?id=1090433 Dec 5 2014, Project MemberThe following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/b6cf19c7b9dd536405c3c4f80876411733c9d5a5 commit b6cf19c7b9dd536405c3c4f80876411733c9d5a5 Author: lgarron <lgarron@chromium.org> Date: Fri Dec 05 02:08:19 2014 Block port 443 for all protocols other than HTTPS or WSS. This addresses the history leak (on non-preloaded HSTS sites) from https://crbug.com/436451 : "If we ask Chrome to load http://example.com:443, it will definitely fail, because Chrome will make plain-text HTTP request to port 443 of the server. However, if example.com is a Known HSTS Host of Chrome (meaning either the user has visited https://example.com before, or it is on the HSTS preload list), it will send request to https://example.com:443, and the request will succeed. We can use JavaScript to differentiate the two cases, since in the first case, onerror event is triggered, while in the second case, onload event is triggered. Therefore, a malicious website can include well-chosen cross-domain images and use this trick to brute-force a list of domains that users have visited. Note that the list could only contain HSTS-enabled but not preloaded websites." BUG= 436451 Review URL: https://codereview.chromium.org/770343003 Cr-Commit-Position: refs/heads/master@{#306959} [modify] http://crrev.com/b6cf19c7b9dd536405c3c4f80876411733c9d5a5/net/base/net_util.cc [modify] http://crrev.com/b6cf19c7b9dd536405c3c4f80876411733c9d5a5/net/base/net_util.h [modify] http://crrev.com/b6cf19c7b9dd536405c3c4f80876411733c9d5a5/net/http/http_stream_factory_impl_job.cc [modify] http://crrev.com/b6cf19c7b9dd536405c3c4f80876411733c9d5a5/net/url_request/url_request_unittest.cc Dec 5 2014,Note: The previous fix doesn't completely address the problem; HTTP over port 80 is blocked by default, but HSTS still redirects. I *thought* I had tested it against the proof of concept, but I must have tested against a different fix. The remaining part is looking bigger than I thought (adding a condition inside URLRequest::GetHSTSRedirect), so I'll have to get to it later. Dec 5 2014,
I don't see a legitimate argument that this is a vulnerability, because unless I'm missing something significant, the issue here is already known. That is, HSTS can leak the fact that a given origin has been previously visited, which is a very coarse piece of information and of limited value. By contrast, CSS :visited disclosed the visited details of whatever URL the attacker chose. And timing measurement still provides a more granular enumeration than the described HSTS approach, with effectively 100% reliability (e.g. http://lcamtuf.coredump.cx/cachetime/) and enumeration of login state for most sites. My suggestion is WontFix and revert that CL, since blocking this kind of enumeration isn't a tractable problem on the Web as it currently exists. Dec 5 2014,Sometimes the domain name itself is valuable to attackers. For instance, people don't want others to know they have visited porn sites. *.xxx domain itself reveals the fact. More importantly, this bug harms the reputation of HSTS. Some sensitive websites will therefore disable HSTS to protect their uses from being tracked by other sites. Dec 5 2014,From docs/asking around, I gathered that while we've given up the war on fingerprinting, we still care about something like this (which is certainly trivial and deterministic). I also didn't find anyone who has particular concerns about blocking HTTP over port 443. If we don't think this fix is warranted, we should update the severity guidelines/Chromium security FAQs with more detail about what kind of history leaks we consider unmitigatable. And we should have something to say to people who would like HSTS to be pretty much an unconditionally good thing for protecting users. (Answers might include "we consider such history leaks just as trivial without HSTS" or "get on the preload list".) Dec 5 2014,I'm sorry, but I think there's too much generalization going on here, rather than focusing on the concrete details of this report. The fact is that this cannot be used to "enumerate recently visited URLs" (many bits). The only information that it provides is whether a user retrieved at least one resource from a given origin (one bit per HSTS origin). Simply put, that information is so coarse as to not be of much use to an attacker, and far more detailed history information can be trivially obtained via resource load timing (a fast, reliable approach that we cannot prevent). My point here is that changing the behavior identified in this report won't do anything to prevent the same or greater information disclosure by readily available means. And we've already accepted the current state as an inherent deficiency of the platform. Moreover, HSTS's potential as a single-bit oracle was already known, which is why we don't retain HSTS state received in incognito sessions. So, unless there's a fix that addresses the larger problem regarding cross origin history probing, I don't see any point in violating the standard to break this narrow case. Dec 5 2014,Which standard does it violate to block HTTP over port 443? As I stated in post #15, not blocking violates the spec. Applications should prevent dereference of a URI that specifies a TCP port number within the "well-known port" range (0 - 1023) unless the protocol being used to dereference that URI is compatible with the protocol expected on that well-known port. I don't think HTTP is compatible with port 443. Dec 5 2014,
Okay, the advisory component of RFC 3986 can certainly be read to supersede the port mapping requirement in RFC 6797. So, I'm fine with landing a patch that correctly blocks HTTP to port 443, regardless of whether or not HSTS is enabled. However, that's a spec compliance issue and still does nothing significant to address history enumeration. So, I'll fix the title and flags, and lucas should land a fix for his patch. Dec 6 2014,#26: Seems like you'd want to change this to only allow schemes to go to ports > 1023 or their assigned IANA port if <= 1023 to match RFC3986? I'm mildly concerned about compat issues with that. Agree that the potential privacy leak of this HSTS approach compared to other approaches is not terribly significant, and if security win of being that restrictive also not too high that this may not be worth it. Dec 8 2014,@ #24: Do we have a document like http://www.chromium.org/Home/chromium-security/client-identification-mechanisms that outlines the currently existing paths to explain whether a user has visited a specific domain across origins? Dec 8 2014,I wasn't previously familiar with that document, but it already covers it: http://www.chromium.org/Home/chromium-security/client-identification-mechanisms#TOC-User-dependent-behaviors-and-preferences Dec 8 2014,So, after talking with Justin on Friday, and looking more into the timing attack website, I've been convinced that timing attacks are in fact basically as easy and reliable as this particular vector. This leaves me with no security argument for blocking HTTP over port 443, so I'm going to revert the change for now. If we think we should re-introduce it (e.g. because it helps avoids bugs or something like that), at least we have the code for it. Dec 8 2014,I think this should be done (not reverted) anyway - the specification asks for it. Dec 8 2014,Another reason to revert: Port 443 is also often used for websockets, to circumvent broken "transparent" proxies that break websockets. See issue 439797 . Dec 8 2014, Project MemberThe following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/3e9300a62343005f995db4a6a72728381993081f commit 3e9300a62343005f995db4a6a72728381993081f Author: lgarron <lgarron@chromium.org> Date: Mon Dec 08 21:28:46 2014 Revert of Block port 443 for all protocols other than HTTPS or WSS. (patchset #7 id:120001 of https://codereview.chromium.org/770343003/) Reason for revert: Unfortunately, this fix didn't do enough to mitigate the original problem (it's easy to tell if a site has been visited). It's also incomplete on its own (i.e. it needs further changes to prevent the HSTS redirect). See https://crbug.com/436451#c30 Original issue's description: > Block port 443 for all protocols other than HTTPS or WSS. > > This addresses the history leak (on non-preloaded HSTS sites) from https://crbug.com/436451 : > > "If we ask Chrome to load http://example.com:443, it will definitely fail, because Chrome will make plain-text HTTP request to port 443 of the server. However, if example.com is a Known HSTS Host of Chrome (meaning either the user has visited https://example.com before, or it is on the HSTS preload list), it will send request to https://example.com:443, and the request will succeed. We can use JavaScript to differentiate the two cases, since in the first case, onerror event is triggered, while in the second case, onload event is triggered. > > Therefore, a malicious website can include well-chosen cross-domain images and use this trick to brute-force a list of domains that users have visited. Note that the list could only contain HSTS-enabled but not preloaded websites." > > BUG= 436451 > > Committed: https://crrev.com/b6cf19c7b9dd536405c3c4f80876411733c9d5a5 > Cr-Commit-Position: refs/heads/master@{#306959} TBR=davidben@chromium.org,phistuck@gmail.com,mmenke@chromium.org NOTREECHECKS=true NOTRY=true BUG= 436451 Review URL: https://codereview.chromium.org/780943003 Cr-Commit-Position: refs/heads/master@{#307340} [modify] http://crrev.com/3e9300a62343005f995db4a6a72728381993081f/net/base/net_util.cc [modify] http://crrev.com/3e9300a62343005f995db4a6a72728381993081f/net/base/net_util.h [modify] http://crrev.com/3e9300a62343005f995db4a6a72728381993081f/net/http/http_stream_factory_impl_job.cc [modify] http://crrev.com/3e9300a62343005f995db4a6a72728381993081f/net/url_request/url_request_unittest.cc Dec 9 2014,
I'm changing the bug title back and marking as WontFix for now. Makes me sad, but 1) it seems to me that Justin is right about the futility of preventing origin-level leakage, and 2) issue #439797 is already one use case this would break. (I've asked for more feedback about this in #439797.) If we want to block HTTP over port 443 for more compelling reasons, I think it should go in a new issue. (And if that happens, I'll follow up and make sure it mitigates this HSTS issue.) |
||||||||||||
►
Sign in to add a comment |
Comment 1 by kenrb@chromium.org, Nov 25 2014
Owner: palmer@chromium.org
Status: Assigned