359 comments

[ 3.0 ms ] story [ 335 ms ] thread
I was surprised to see that the cost of another connection wasn't thrown in as a downside. With http2, that cost goes away if the resource is stored at the same domain as the site.
That's a really good point now - previously an advantage of using a CDN was that a browser would request a limited number of concurrent connections per domain, so having the static assets spread across domains sped up that initial page load. As HTTP/2 spreads the cost is indeed reduced.
Loading common libraries from a CDN will no longer bring any shared cache benefits, at least in most major browsers. Here's Chrome's intent to ship: https://chromestatus.com/feature/5730772021411840 Safari already does this, and I think Firefox will, or is already, as well.
Oh, that's interesting. I guess it makes sense from a security and privacy perspective.
Will this cause performance issues for sites that use static cookieless domains for js, images etc

Google themselves do this with gstatic.net and ytimg.com etc

> Will this cause performance issues for sites that use static cookieless domains for js, images etc

> Google themselves do this with gstatic.net and ytimg.com etc

Most probably not. The point of cookieless domains is that you can use a very simple web server to serve content (no need to handle user sessions, files are pre-compresses and cached, etc.) and it lowers incoming bandwidth a lot. If you have a lot of requests (images, css, js) the cookie information adds up quickly.

Opening video thumbnails from ytimg.com will still be cached for youtube.com as before. The only thing that will change is for embedded videos on 3rd party websites as those won't be able to use caches ytimg.com thumbails from elsewhere.

Couldn't the same thing be achieved by routing e.g. google.com/static/ to a separate simple webserver, instead of using another domain? Or use a subdomain, e.g. static.google.com.

The current way seems like needless DNS spam to me...

Even if Google used a separate highly optimised webserver for google.com/static/jquery.js, users who are logged in would be sending their auth cookies when requesting the library.

Given that generally people have slower upload than download, shaving off a few bytes from requests is worth it.

I also recall that browsers [used to (?)] limit concurrent requests per domain which this helps work around

I wonder if an nginx plugin could be made to auto-cache CDN javascript/css files and edit the HTML on the fly to serve them from locally.
You’re probably thinking about a caching proxy like squid cache.
You can set up a path that does a cached proxy to the CDN and just edit the HTML yourself. It's a bit annoying to get the cache settings to work properly, but editing the HTML is easy.
I'm not sure I understand the threat here. Say I visit SiteA which references jQuery from CDNJS, then later visit SiteB which references exactly the same jQuery from CDNJS - what's the problem?
It can be used to track users across domains to some degree.
Not to be dense but wasn't that always the purpose of running a CDN service for common scripts and libraries?
The script wouldn't have to be from a CDN to track people using the browser cache. I could infer whether you've visited a site that doesn't use CDNs or trackers by asking you to load something from that site and inferring whether you have that resource cached by the time it took you to load it.
This is true, but if you're running a CDN you have access to cross-domain user information just based on the headers, no?
The CDN is not the place you have to worried about.

If Site A loads a specific JavaScript file for users with an administrator account, Site B can check to see if the JavaScript file is in your cache, and infer that you must have an administrator account if the file is there.

The attack can happen with different types of resources (such as images).

This I understand, the risk of third-parties monitoring. The attacks are pretty obvious. My confusion is over what the business model of a commercial CDN is if not to track users across multiple sites? How do they pay for bandwidth?
The problem is not the CDN (or arbitrary shared source domain) being able to track you but the sites which use the CDN.

Furthermore a CDN can't track you as simple as you might think, it often would require thinks which need explicitly opt-in agreements on a per website basis to be legal.

Furthermore due to technical limitations you can only get that permission from the user after the CDN was already used.

CDNs can still track aggregated information to some degree but they can't legally act like a tracker cookie.

Thanks, I understand the issue now - I haven't thought about CDNs from a privacy perspective before.

I suppose with HTTP2 some of the benefits of serving JS through CDNs are gone anyway, so I guess it's time to stop using them.

How serious is this type of threat? Compared to all the info about us that is already shared by data brokers?
There are several data-broker-esque "services" that actually do this already with FB, Google, etc assets (favicon.ico and similar, loggedIn urls, ...) to check whether you have visited those pages, or whether you are logged in to those services by trying to request a URL that might return a large image if logged in, or fail rapidly if logged out. -- This has been a thing for a long time: https://news.ycombinator.com/item?id=15499917

If you don't use any of those sites, you're considered higher risk/fraudulent user/bot.

Here's an example of a very short and easy way to see if someone is probably gay: https://pastebin.com/raw/CFaTet0K

On chrome, I consistently get 1-5 back after it's been cached, and 100+ on a clean visit. On Firefox with resistFingerprinting, I get 0 always.

Thank you, that was insightful.

> Here's an example of a very short and easy way to see if someone is probably gay

Ok, but now the resource is in my cache, so from now on they will think I'm gay?

You could have run this on a private window of the browser (and in that case, they would surely think you're a closeted gay).
> Ok, but now the resource is in my cache, so from now on they will think I'm gay?

This resource is just generic, so probably not, but if you actually visited grindr's site without adblocking heavily, they load googletagmanager and a significant number of other tracking services, which will almost certainly associate your advertising profile and identifiers as 'gay'

I also can't believe they send/sell your information to 3 pages worth of third party monetization providers/adtech companies for something that is this critically sensitive.

Could this not be solved by by Grindr setting up CORS properly for that resource? It's unlikely anyone would ever open the script directly in their browser.
Fun fact: browsers put scary warnings in their dev console (and some web sites log warnings or console) because some people love copy-pasting code they got from sketchy people trying to bypass all the browser security.
CORS wouldn't help here. CORS prevents you from reading the response or making a cross origin XHR requests, not loading an external resource from a different domain in a script or img tag.
(comment deleted)
It's being actively used by the ad networks to do user fingerprinting instead of cookies, since the latter are more and more blocked.
I guess as serious as any other privacy threats but one that doesn't get enough attention in my opinion. CDNs and web fonts are definitely being used to track us and can bypass mitigations like private mode in your browser and ad/tracker blockers by tracking your IP address across sites.
maybe not with CDNJS, but perhaps you don't want every website to know you have AshleyMadison.com assets cached.
Can websites even tell what is cached and what’s pulled fresh?
Yes, using timing
Not if the javascript starts running only after all resources have loaded.
No there could still be timing attacks after. Just dynamically request a cross domain asset
Then those requests should not be cached?
(comment deleted)

  const start = window.performance.now();
  const t = await fetch("https://example.com/asset_that_may_be_cached.jpg");
  const end = window.performance.now();
  if (end - start < 10/*ms*/) {
    console.log("cached");
  } else {
    console.log("not cached");
  }
In that case, the browser would always load the asset (it is not cached). So the rule would be that only stuff that is directly in the <head> may be cached (or stuff that is on the same domain).
To be clear, the context of the thread is "why do we need to partition the HTTP cache per domain." My example code works under the (soon-to-be-false) assumption that the cache is NOT partitioned (i.e. there is a global HTTP cache).

> In that case, the browser would always load the asset (it is not cached).

Agreed, if the cache is partitioned per domain AND the current domain has not requested the resource on a prior load. If the cache is global, then the asset will be loaded from cache if it is present: https://developer.mozilla.org/en-US/docs/Web/API/Request/cac...

> So the rule would be that only stuff that is directly in the <head> may be cached (or stuff that is on the same domain).

You could be more precise here: with a domain-partitioned cache, all resources regardless of domain loaded by any previous request on the same domain could be cached. So if I load HN twice and HN uses https://example.com/image.jpg on both pages, then the second request will use the cached asset.

> To be clear, the context of the thread is "why do we need to partition the HTTP cache per domain."

Ah right, the thread is becoming long :)

> So if I load HN twice and HN uses https://example.com/image.jpg on both pages, then the second request will use the cached asset.

Good point!

Wouldn't the act of timing a download mean that I download and pollute my cache with new assets from the site trying find where else I've been? Does this only work for the first site that tries to fingerprint a browser in this way?
Is there a noCache option? Or can JS remove entries from the cache to reset it?

Someone below mentioned doing requests for a large image that requires authentication. Short response time means the user isn't logged in (they got a 403), long response time means they downloaded the image and are logged in.

I'm guessing many websites are identifiable by which patterns of libs and specific versions they will force you to cache. One SiteA would then be able to tell that a user visited a SiteB (which, depending on the website, may or may not be problematic)
I'm sure some sites would be identifiable by their cached libs, but the cache is shared, so any overlapping dependencies would decrease the accuracy to unusable levels. The best you could do is know someone did not visit a site in the last ${cache_time}.

There are, of course, other vectors to consider, but I can't think of any that could be abused by third parties. If anything, isolating caches would make it easier for the CDN themselves to carry out the attack you mentioned, as they would be receiving all the requests in one batch.

What if my website tries to load a JS file that only foxnews.com loads (maybe with a less restrictive CORS config)?

I'd be able to tell if you visited Fox news recently, correct?

It would be extremely unlikely for only one site to use a specific file from a public CDN (like cdnjs). As for site-specific files like JS bundles and other static assets, those would be served on a "private" CDN, usually under the same domain (like cdn.foxnews.com) and with restrictive CORS settings for this very reason (and also to prevent bandwidth stealing).
A single file? Highly unlikely.

But three specific files can already be pretty unique. I chart.js with two specific plugins in my toy project, and I'm willing to bet that no one else on the world uses the exact same set and version configuration.

Exactly, but a third party can't see that set from the cache, they see the union of every website recently visited. They would see hundreds of files from many websites and if only one of those uses one of the three files yours does, it's impossible to tell for sure without a file that isn't used anywhere else on the Web. Your site uses A+B+C, site 2 uses A+D+E, site 3 uses B+F, site 4 uses C. The cache contents is A+B+C+D+E+F+... did the user visit your site? It's like trying to get individual pictures out of a single piece of film that was exposed multiple times - you can make some guesses and rule some possibilities out, but nothing other than that will be conclusive.
You only need ~30 bits of information to uniquely fingerprint someone.
Could you explain this further?
There are about 8 billion people. 33 bits is enough to give each one a unique number. A whole bunch of them doesn't have access to the Internet, so fewer than 33 bits are enough to identify someone on the Internet.
Try and load assets from another domain and observe if it was probably cached or not, and you can know that they visited the site
I guess, but that disadvantage seems massively outweigh by the benefits. Can always use something like [1] to check if a client is active on interesting sites.

[1] - https://www.webdigi.co.uk/demos/how-to-detect-visitors-logge...

Can’t always use that. It’s much less specific compared to the potential of cache, only works when websites provide that type of redirect, doesn’t work if you block third-party cookies (I think a form of that might already be the default in some browsers), etc.
Are there actually any benefits though? I saw an article a few years back about how when loading jquery from Google's cdn there was about a 1% chance the user had it cached already. Since you have to have, the same library, the same cdn source and the same version of the library, it almost never is the case that the user has already grabbed this recently enough that it hasn't been kicked out.

Plus the trend now is to use webpack and have all of your deps bundled in and served from the same server.

Wouldn't a better, but partial, solution be for browsers to preload the top x common libraries? All other libraries would probably have to follow this new rule.
Please no, don't create such barriers.
Every other language runtime has a standard library, it's always been a shortcoming of the web IMO
At that point wouldn't it make more sense to just have the browsers include that functionality?
As soon as you add something to JS you have to support it forever. Its better to let sites pick what they need and scrap what they no longer need. JS has already added most of the useful stuff from jQuery. If browsers included a built in version of React it would pretty much lock in the design as it is now without room to remove and replace bad ideas.
What version(s) of those libraries? I mean, I don't deal with this anymore... but I've seen sites literally loading half a dozen different copies of jQuery in the past (still makes me cringe).
On the other hand, the URL for a common library hosted on cdnjs (or one of the other big JavaScript CDNs) and included on many different websites is much more likely to already be cached on edge servers close to your users than if you host the file yourself.
The time to connect to the CDN hostname will negate any benefit, especially if push can be used.
You can mitigate this by getting your website itself on a CDN. If this is cached, then it's assets (incl javascript), would be too.

And by going that route you make sure that all pieces of your website have the same availability guarantees, the same performance profile, and the same security guarantees that the content was not manipulated by a 3rd party.

> And by going that route you make sure that all pieces of your website have the same availability guarantees, the same performance profile, and the same security guarantees that the content was not manipulated by a 3rd party.

You can already guarantee the security of the file by using the integrity attribute on the <script> tag. And the performance of your CDN is probably worse than the Google CDN (not to mention that you lose out on the shared cache).

I agree on the security side if you use that attribute. However:

> And the performance of your CDN is probably worse than the Google CDN

What means probably? Other CDNs (Akamai, CloudFront, Cloudflare, etc) are also fast.

And by pushing one piece of your website on a different CDN you force your users browser to create an additional HTTPS connection which takes additional round-trips, instead of being able to leverage one connection for all assets. This alone might as well outweigh the performance differences between CDNs.

Also the "shared cache" benefit might go away, if I read the other answers in this topic correctly.

Does someone know why they don't split the cookie storage equally by the top origin?

I mean, wouldn't that take care of a whole class of attack vectors and make cross-origin requests possible without having to worry about CSRF?

One of the problems is that it breaks use cases like logging into stackoverflow.com and then visiting serverfault.com, or (if you do it by top-level origin) even en.wikipedia.org and then visiting de.wikipedia.org. [1]

While privacy sensitive users may consider this a feature in case of e.g. google.com and youtube.com, the average user is more likely to consider it an annoyance, and worse, it is likely to break some obscure portal somewhere that is never going to be updated, so if one browser does it and another doesn't, the solution will be a hastily hacked note "this doesn't work in X, use Y instead" added to the portal. And no browser vendor wants to be X.

[1] The workaround of using the public suffix list for such purposes is being discouraged by the public suffix list maintainers themselves IIRC, so the "right" thing to do would be breaking Wikipedia.

Edit: If done naively on an origin basis right now, it would break the Internet. You couldn't use _any_ site/app that has login/account management on a separate host name. You couldn't log into your Google account with such a browser anymore (because accounts.google.com != mail.google.com). Countless web sites that require logins would fail, both company-internal portals and public sites.

It's possible to get around this with a redirect staple. E.g. if Google wants you to be logged in on youtube.com and google.com simultaneously:

1) User logs in at google.com/login and sets google.com cookies. 2) Server generates a nonce and redirects to youtube.com/login?auth=$NONCE 3) youtube.com checks the $NONCE and sets youtube.com cookies 4) youtube.com redirects back to google.com.

Firefox's container tabs can maintain isolation despite this since even this redirect will stay within a container. However there is a usability penalty since the user has to open links for sites in the right container (and automatically opening certain sites in certain containers will enable cross-container stapling again).

"if"?

webapps.stackexchange.com/questions/30254/why-does-gmail-login-go-through-youtube-com

Good! The whole idea for doing js on CDN is suppose to make it easier for entry level front dev to be able to start coding. I think that is great for a school exercise, should never be used in business or production sites.

And on a side note, very unhappy about how the entry to be a developer has lower significantly over the last 10 years or so.

Weirdly, the piece fails to mention my number on reason for loading libraries from a CDN: economy. Least setup cost and externalized hosting costs mean that unless I have good reasons otherwise, I have to use them from an economic perspective!
Where is your webpage hosted that makes it not require setup or hosting costs compared to scripts served from the same place?
You probably want to tell me that adding a few megabytes extra is not going to cost more from the hoster. In most instances this is true. What you forget is that everything has to be configured and maintained.

Just ensuring the lib gets deployed to the right place, in the right version, and with the right caching-headers, has cost me hours of my life in some instances. When I let a CDN do it, it was minutes.

Those days are over. I don't load static libs any more, so the question doesn't even come up. Except maybe for fonts.

CDNs are misunderstood these days. Caching at the browser across sites is not that important, it caching at a point of presence (POP). This POP being so much closer to your end users brings performance gains because TCP is terrible over distances. QUIC may fix this by it's shift to UDP. I haven't seen a benchmark yet.

Security is a concern, use SRI.

Reliability can be mitigated with fail over logic to a backup.

The part missed is bandwidth. Using a CDN means your web server doesn't have to serve out static files that you are paying per a GB to serve. Small sites it's not much but it does add up. It's a Content Delivery Network not a Cache Delivery Network.

Wait, what is the misunderstanding? Aren't these the well known benefits of CDNs?
(comment deleted)
CDNs are just a service to handle delivery of static content for you. Their main points (unordered) are:

- reliability

- delivery speed through closeness to user (having nodes all around the world)

- cost

- ease of use

- handling of high loads for you / making static content less affectedly by accidental or intentional DoS situations

That multiple domains might use the same url and might share the cache was always just a lucky bonus. Given that the other side needs to use the exact same version of the exact same library with the exact same build options accessed through the exact same url to profit from cach sharing it never was reliable at all.

I mean how fast does the JS landscape change?

Given how cross domain caching can be used to track users across domains safari and Firefox disabled it a while ago as far as I know, and chrome will do so soone.

My understanding is that it’s not TCP but the tcp AND tls handshake overhead combined. Where-as quick combined both handshakes at the protocol level.
TCP has the concept of a TCP Window, where its a buffer of the data that the opposite side waits for an ACK packet from before sending more data. Windows defaults to 64kb to start. On your local LAN (which TCP was built for), no big deal, but going across a distance. Then add in one lost packet or one out of order packet and TCP has to ask for it again and delay the whole thing. Its why HTTP/2 has higher latency on spotty 4g networks. The TLS handshake suffers from the same distance issue ACK packets have which with TLS 1.3 there is 0-rtt which removes the handshake as part of the first TCP packet.

QUIC puts everything in UDP, so theoretically its a never ending firehose of data for a download with the occasional "hey, I missing packet 3, 12, 18, please resend". Mimicking TCP but putting the app in control versus the kernel.

Quic also has a window size.

> QUIC congestion control has been written based on many years of TCP experience, so it is little surprise that the two have mechanisms that bear resemblance. It’s based on the CWND (congestion window, the limit of how many bytes you can send to the network) and the SSTHRESH (slow start threshold, sets a limit when slow start will stop).

https://blog.cloudflare.com/cubic-and-hystart-support-in-qui...

Quic kind of has 3 windows:

Per stream and per connection flow control windows, which kind of indicate how much data the peer may send on a given connection before it gets a window update. Those windows also indicate how much the server is willing to store in its receive buffers, since the updates are likely sent when those buffers are drained.

A congestion window, which indicates how many low-level packets and data in them can be in-flight without being acknowledged. Those also account for retransmissions, and packets which do not necessarily contain stream data.

One won’t have loading time problem if one doesn’t ship websites with kilotons of JS crap. Also paying per downloaded content is dumb as it’s easy for an attacker to attack you financially and lot of hosting companies (like OVH) offers "unlimited" bandwidth.
The post is "Please stop using CDNs for external JavaScript Libraries" not "Please stop using CDNs". If a CDN is critical to your site's performance you should put your site on it not internal libraries here and external libraries there. The page mentions this as well:

"Speed:

You probably shouldn’t be using multi-megabyte libraries. Have some respect for your users’ download limits. But if you are truly worried about speed, surely your whole site should be behind a CDN – not just a few JS libraries?"

Also even before QUIC HTTP/2 fixed a lot of the problems with distance as you no longer need to wait for separate handshakes for multiple files to be streamed. QUIC will still give a few advantages but again those advantages would be good to have on your whole site not just a few libraries.

But wouldn't a site be faster if cachable requests go to a cdn, and no un-cachable requests go directly to origin with no forwarding at the cdn layer?
When I say "If a CDN is critical to your site's performance you should put your site on it" I do mean to say "put (those parts of) your site on it" not "if 51% of your site should be on a CDN put the other 49% as well".

But to your question though even un-cacheable content can be "those parts of". There are products from CDNs like https://blog.cloudflare.com/argo/ which combine CDN cache tiering with higher tier network transport to origin servers for all cache misses (or uncacheable content). Again though, it depends on if it's critical to your site's performance or not. If you don't have a bunch of uncacheable content, that content doesn't need the absolute best transport, or the time/money could improve some other part of the site speed more then it's not critical to your site's performance.

Only if the CDN serves assets faster than the origin can. That's not necessarily true by default. Not to mention, if the speed is okay for the initial request, why not the following requests?

The final nail in the coffin for me is that CDNs are a shared resource, if your CDN is getting heavy traffic or otherwise suffering, it becomes your slow point, while your site is fine. I just don't see any upsides worth the tradeoffs.

If you have scale that demands some serious content distribution, that is different, I would argue you shouldn't be relying on public shared CDNs then even moreso. Pay for a CDN service or roll your own.

CDNs can almost always serve assets faster than the origin can.

Because PoPs are closer and transfer speed ramp rate scales to latency, the further away a server is, the longer it takes the download to ramp up to full speed. This is especially relevant when talking about smaller resources like javascript, css, and small or optimized images, and webfonts.

How would failover actually be implemented? If you have a script tag with integrity enabled, and the cryptographic hash doesn't match, what happens next?

From some quick research, it doesn't seem like the script tag has built-in support for this. One could imagine something like multiple src attributes (used as a search order for the first valid file), but that doesn't seem to exist. So it seems like the web page has to do it manually.

Which I guess means you have to have some javascript (probably inline, so you know it's loaded and for performance?) to check and fix the loading of your other javascript.

If it's really that manual, it sounds like it adds cost to implementing this correctly. in other words, it might be one of those scenarios where correctness is achievable, but it's a whole lot simpler to just not do it that way.

Since Script tags are blocking, you can do a undefined check then if that fails, inject a new script tag either local or a secondary CDN.

Link for reference. .Net Core has this built in as a tag helper too! https://www.hanselman.com/blog/cdns-fail-but-your-scripts-do...

Thanks. So it seems like it's not really that bad. Particularly if you are already using some loader tools (and don't have to add them to your build just to get this).
> This POP being so much closer to your end users brings performance gains because TCP is terrible over distances. QUIC may fix this by it's shift to UDP. I haven't seen a benchmark yet.

Quic can't defeat physics. Performance will still lineary degrade with distance to (edge) servers, and therefore CDNs will stay important.

What Quic however will do is reduce the time-to-first-byte on an intial connection by 1RTT due to one less handshake - which can be e.g. a 30ms win. After the connection is established it aims to yield more consistent performance than e.g. HTTP/2 over TCP. But packets will still require the same time to go from the browser to an edge location, and therefore the minimum latency for a certain distance is the same.

(comment deleted)
Sure, but if you have any custom JavaScript that needs to load before the page can render, using a CDN for just the JS libraries does not really help.

If you want the benefit of a CDN, you need to put your own code up there. And if you’re doing that, you might as well host your own copy of the libraries too, so the browser won’t have to talk to two different CDNs.

We bundle everything, including dependencies, up into minified modules and serve our whole frontend via Google Cloud's CDN, using Cache-Control to control the way things are cached. This way there are no third-party requests at all to load the site, and everything is cached close to the user.
Extensions that act like a local CDN. Page loads faster, more privacy, etc.

https://decentraleyes.org/

https://www.localcdn.org/ (fork of decentraleyes, with many more resources)

Interesting, I was already using DecentralEyes. Do you know why the fork happened? What policy does DecentralEyes keep that LocalCDN extends or violates?

edit: will be sticking with the original, looks like the fork maintainer made no effort to work upstream first, which is a very bad look for what is essentially a piece of security software. https://gitlab.com/nobody42/localcdn/-/issues/5

localCDN has more resources than Decentraleyes. It also has very important resources like Google Fonts, some cloudflare resources etc, none of which were present in Decentraleyes (the last time I checked)

> will be sticking with the original

It's your choice. The fork is better. The maintainer seems a bit more active (more updates) and extremely pro-privacy (I concluded this from his home page and extension settings)

I was thinking about switching from decentraleyes too, but I'm hesitant to install a "can access all sites" extension that hasn't been vetted ("recommended") by Mozilla.
LocalCDN is the most offline extension I have seen.

It even opens donation pages locally, instead of opening the author's website. He says ''I think it is better if your public IP address is rarely listed in any server log files.''

localCDN looks super interesting. It works with Chrome partially and fully with Firefox. I'm curious, is there any good native tool to replace localCDN, uBlock, uMatrix ( resources concern ). Btw, thanks for pointing out localCDN
I don't know any replacements, but localCDN can generate rulesets for uBlock Origin and uMatrix if you have configured it too hard (had mode,etc)

You must enable the rulesets. It's very easy and a one time job. To generate them, go to LocalCDN settings and select your adblocker.

I found out about localCDN recently, commented on this subject, and got this response from the author:

https://codeberg.org/nobody/LocalCDN/issues/51#issuecomment-...

I haven't gone searching for a PR yet and didn't think to do so beforehand (all made more complicated by both projects' repos having moved locations at least once recently).

Update: searched for a PR, didn't find it.

Initial commit[1] in the LocalCDN repo is Feb 2020; I don't see any PRs on either of the Decentraleyes repos in early 2020 or late 2019. Of course, it's still possible the author reached out

[1] For some reason, the project does not continue the git history from Decentraleyes. For me this is a red flag (much easier to sneak in a change this way) and I will continue using Decentraleyes.

I use decentraleyes and like it, but although it has a ton of content, much was outdated and there were no updates. It would also be nice to be able to add resources yourself.
There are some good reasons in here (especially privacy), but I'm not convinced by the security point. It seems like the example linked about British Airways was JS under the britishairways.com domain being changed, not a third party CDN.

Incidentally, a few years ago when people were loading third party scripts over HTTP, I demoed a fun hack where, if you control a user's DNS, you could redirect queries to popular CDNs to a proxy that injects keylogger code and tells the browser to cache it indefinitely. Because at the time almost every site included either jQuery or Google Analytics, you'd have a persistent keylogger even after the user switched to a more secure connection. How far we've come!

Is that demo of yours available somewhere? It sounds interesting and I'd love to read up on it
I was able to find a video of it: https://www.youtube.com/watch?v=_BUg9NzdLd4

I think this is the code: https://github.com/paulgb/cachebeacon

It basically just runs two servers:

- A DNS server that resolved a list of domains to its own IP.

- An HTTP server that looked at the HTTP host and proxied requests to the upstream server. If the content type or extension indicated that the response was JavaScript, it would add the payload and set cache headers to cache as long as possible.

- A special HTTP endpoint on the proxy server to capture data sent back from the payload.

Just wondering. Can "integrity" be used instead of url to provide better caching? Because it is based on hash, which should be more reliable than just url.
That would be a super-cookie though.
How would specifying what content the library should have (to preserve integrity) lead to tracking?
Caching across sites at all means you can see if it loads quickly or slowly to make inferences. Non-obvious tradeoff imo.
It's not about how fast it loads, it about whether it actually requests the file from the server or not.

See my comment above, if you have "x.js" in your cache and it's your first time on this site, it means that you previously visited another site that contains "x.js".

In cache - high speed, fetch from server - low speed. Isn't that how you detect it?
The problem is not that you can detect it on the client,but you can also see it on the server.

On the server you see that user requested the HTML for the page and other resources, but never requested X.js.

You could keep a count of requests, per user per file.

A number of problems surface [0]. Starting with timing leaks.

> The first difficulty of implementing cross-origin, content addressable caching on the Web platform is that it may leak information about the user’s past browsing. For example, site "A" could load a script with a given hash, then later when the user visits site "B", it could attempt to observe the time it takes to load that same resource and infer whether the user had previously been at "A".

[0] https://hillbrad.github.io/sri-addressable-caching/sri-addre...

Might subresource integrity be what you are looking for? Not sure if you meant that, or if you are proposing such a system (which is a good idea!) without realizing that this already exists:

https://developer.mozilla.org/en-US/docs/Web/Security/Subres...

He is proposing to use the integrity hash for caching purposes, not for security purposes.

This has been discussed before and it is unlikely it will be implemented as it is almost impossible to eliminate finger-printing and privacy issues if this cache is implemented plus it might lead to security problems:

https://github.com/w3c/webappsec-subresource-integrity/issue...

> for caching purposes, not for security purposes

Whether it's for caching or security purposes, a hash of the contents is a hash of the contents.

The link you shared is about tracking whether a given document is already cached. That's the same problem as with normal caching. At least the top N libraries could be downloaded with a browser update and be equal for everyone.

(comment deleted)
Not sure if you are referring about domain-local cache, or a cache shared between domains: https://github.com/w3c/webappsec-subresource-integrity/issue...
A privacy issue example if a shared cache is implemented: jQuery v2 is installed and shared between sites A,B,C. If user goes to site B for the first time and the file is in cache (never requested from it), then it means the user has also visited A or C. If more files like this are shared, then, based on the requested files you can somewhat estimate which websites a user visited before.
npm, bower etc more or less killed this pattern I thought?
You'd be surprised how many new websites out there are still made by importing JS from a random CDN, no version control of course, code is uploaded manually through FTP
I think we should be amazed at how far amateurs can go :)

In a world where all websites have to be perfect, we would have far fewer websites.

What does Wordpress do ?
I think this is like asking "What would Jesus do?", but with a worse role-model.

The problem with WordPress is a lot bigger with the Themes/Plugins than with the platform itself.

During code review we just discovered that a somewhat popular npm library used a CDN for its assets (images of flags corresponding to country iso codes), forcing us to reimplement without the library.
We need another new competing module system to screw up all the CDNS!
A DNS lookup will also affect speed
Not to mention another TCP handshake, TLS handshake, certificate validity checks, etc.

HTTP/2 changed everything. Now the fastest way to serve your assets is to push them down the pipe that has already been set up.

> Latest software versions automatically when the CDN updates.

I'd also be very careful including a JS library from a CDN that gets auto-updated to the latest version, this might break your site without you noticing.

We did this with our own JS library (Klaro - https://github.com/kiprotect/klaro), for which we offered a CDN version hosted at https://cdn.kiprotect/klaro/latest/klaro.js. We stopped doing that with version 0.7 as we realized we could not introduce any breaking changes without risking to break the websites of all users that were using the "latest" version. So what we do know is that we only automatically update minor releases, i.e. we have a "0.7" tag in the CDN that will receive patch releases (0.7.1, 0.7.2 , ...) and which users can safely embed in their websites.

That said we always recommend users to self-host their scripts as well and to use integrity tags whenever possible, it's usually better from a security and privacy perspective as well.

Like most JS libraries Klaro is quite small (45 kB compressed), so the loading time is dominated by the connection roundtrip time, which again is dominated by the TLS handshake time. For our main servers in Germany we have a roundtrip ping of around 20 ms (for most connections from Germany), and with the TLS negotiation it takes about 60-100 ms to serve the script file from our server. If the server connection is already open that time reduces to 20-40 ms. So the benefit of hosting small scripts with your main website content is that your browser doesn't have to do another TLS handshake with the CDN, which for small scripts can dominate the transfer time. If you then use modern HTTP & TLS versions you can reduce the transfer time quite a bit.

The CDN can’t swap the files under us, if we activate the CSP, content security policies, which are activated once per domain. They require to provide <script> tags with the “integrity=“ attribute holding the hash of the file.
That will still break your site if the CDN updates the file because it won't load anymore. The point the author made was that people use libraries distributed by CDNs to get auto-updates and fixes, sadly that won't work in combination with CSP as you'll still have to update the integrity tag manually.

We deliver integrity tags for our CDN files as well btw (https://github.com/kiprotect/klaro/blob/master/releases.yml), that doesn't solve the auto-updating problem though.

Fun case from Sweden: the central government website for healthcare included js from a third party with no integrity hash. Third party got hacked and they changed the js to mine cryptocurrency over the weekend. Thousands of people participated in mining...
This is where browsers need to be locked down a bit more. The web api, and available local compute resources should be governed by permissions. Yeah it'll break a few sites at first!
It would be sufficient to just put a red triangle on the tab pointing out this one tab is using high cpu.
>But if you are truly worried about speed, surely your whole site should be behind a CDN – not just a few JS libraries?

Does it have to be all one or the other?

I feel like this kinda hand waves away an advantage.

> British Airways' payments page was hacked by compromised 3rd party Javascript. A malicious user changed the code on site which wasn't in BA's control – then BA served it up to its customers.

That's the only reason you should ever need to not load any 3p javascript including google-analytics.

If you're like me and want to avoid loading popular javascript libraries from CDNs but want those webpages to work, get the Decentraleyes plugin: https://en.wikipedia.org/wiki/Decentraleyes

Or at least do subresource integrity so you know you’re getting the exact same file every time.
That can mitigate the damage, but any user with a browser that doesn’t support it will still be compromised.

If it’s an important part of the site, it might make the failure more obvious in newer browsers.. but small libraries used on only some pages might not be noticed quickly... so you’d probably also want to test all of your resources regularly.. and even then the time between those test runs may allow some users to be compromised.

So using it is a good idea but it’s not a fix for the actual problem.

https://caniuse.com/subresource-integrity

It is basic available for every browser except IE and Opera Mini, so I think it is user's problem to use an old browser that don't support a wide supported security feature.

Might as well simply block IE users outright at this point; it's just not worth the risk (and classic edge is close). It's probably better user experience to be upfront about issues than pretend you actually test and support all those old versions (unless you do... but why?)
(comment deleted)
Your own link says 94.79% coverage. So 1 in 20 users would be compromised.. on a large site that could be millions of users.

And your response is: "that's their problem" ??

I hope you're not in charge of any important or large sites or anything that handles financial data (ecommerce, etc)... because this isn't a good attitude when it comes to security.

By the same logic, TLS 1.2 isn't a solution to insecurities in 1.1 because only 98% of users currently support it.

It's perhaps worth accepting there's no silver bullet here but a combination of initiatives like SRI is still worthwhile to help reduce the attack surface for the majority of users?

Simply enabling TLS 1.2 is not a fix for problems in 1.1. You must also disable 1.1 in your server config. It's both actions that fix the insecurities: first enabling a secure method of communication; and then cutting off anyone trying to communicate insecurely. If you simply enable 1.2, but leave 1.1 working, then you haven't fixed the problem.

SRI is the equivalent of just enabling 1.2. You haven't disabled access to browsers that dont support SRI.

You 2nd sentence sounds remarkably similar to my first post that maple responded to: SRI can help mitigate the damage, but it cant fix it.

You seem confused about the difference between mitigation and fixing.

Mitigation: the action of reducing the severity, seriousness, or painfulness of something.

Key work there is reducing. A fix actually eliminates the issue.. like enabling 1.2 + disabling 1.1 eliminates the potential for communicating insecurely.

It's important to understand the difference because anything short of actually fixing the issue leaves some users exposed to the vulnerability.

Except this article and that quote are wrong. If you look at the linked report on the British Airways hack, https://www.riskiq.com/blog/labs/magecart-british-airways-br..., you'll see that the compromised script was hosted on www.britishairways.com. It wasn't a third party CDN hack, their own CMS was simply compromised.

I kept reading this article looking for an actual decent reason to not use a third party CDN, and I never found one. In fact, the right answer is really that you should always set up your CSP and subresource integrity rules to completely prevent these kinds of attacks, whether from an unintended script injection from your own domain or a 3rd party.

Should you also stop serving "analytics.js" from Google's website and serve it from your own website?
Yes.

(Matomo is your friend.)

This is fking awesome. Thanks! I put switching to Matomo on my TODO.
If you like Matomo you might also want to have a look over https://usertrack.net, but depending on the features you are interested in, the free version of Matomo might be better for you.
Just block JS entirely. 99.99% of sites are worthless anyway, its not hard to enable JS on sites you want to use
I use NoScript in Firefox. It floors me how many folks use a slew of third-party assets hosted on CDNs I'venever heard of, many of which aren't even critical to the site's functionality.
Plus, you save CPU, memory, bandwidth and electricity.
I want to discuss a (minor) antipattern that I think is (slightly) harmful.

It's caching, not cacheing.

Non-native speaker here. How is that an "antipattern" and not simply a spelling mistake?
That + the word "harmful": it's probably a parody of HN-speak. I hope.
I'm just copying the language from the beginning of the article.
Some of the time, web developers are using CDNs because they want to save 5 minutes from downloading and hosting it on their own servers, or they want to not have to add it to their package.json. I've noticed this with novice web developers, where they just want to get something working up as fast as possible. This practice of linking to a CDN or external URL for a js library used to be heavily discouraged, called "hotlinking", but it seems like many javascript libraries now are fine with it (even encouraging it).

I see a similar problem with Google Analytics or Google Fonts, where you're sacrificing user privacy and agency in exchange for developer convenience. In a slightly more privacy-centric era (perhaps in a few years), I think people will consider this practice unethical, as the web developer is sending their visitors off to fetch and run javascript code from a third party's computer. The security, usability, and privacy problems noted by the article are not worth the few minutes saved by the developer.

Let us say that you work for or own a website with 1m unique visitors per month. Let us assume your average JS library size that can be cached and outsourced totals 400 kilobytes (that's on the low end).

1 million * 400 kilobytes = 400 gigabytes

For AWS the entry level tier per gigabyte is $0.15/GB.

That is a whopping $60 USD per MONTH that you can save by running your scripts on a CDN.

So, yeah.

And someone is paying that...
The users are paying for it with their privacy.

CDN providers are not charities: they sell traffic metadata.

that's what I meant, million data points costs $60
Why are you serving 400kb of JavaScript? Why are you paying $0.15/GB?

These numbers are certainly not on the low end.

I assume that it's "for the sake of argument"

https://www.ldoceonline.com/dictionary/for-the-sake-of-argum...

Also it's a bit like a proof. By assuming worse numbers, and arriving at $60, your better numbers will only prove the point even more.

By arriving worse numbers, you arrive at a higher price, so it makes your point weaker if anything. If a site bundles half a meg of JS, it's going to be unpleasant to use, and if you're serving so much JS it's hurting you financially, it's probably time to reconsider what you're doing.
No it makes the point stronger, the point being "it's cheap enough to host your own CDN that you don't need to use a free one". And further "it is so so cheap that you can still make these dumb mistakes and it's still worth hosting the files yourself!"

It's like saying should I use a dry cleaner? Well it's only $50 to get them to iron my shirts, so it's worth it for my time saved. Then someone says "$50! You are being ripped off, if your "doing it right", you'd get it done for $10", to which I say, well if it's worth me paying $50, then it is worth me paying $10, so my original argument "use a dry cleaner" is made stronger.

I don't think it is done to save money. I reckon it's done because the MVP tutorial they used when they got started used the CDN. Or they are using Wordpress and the theme decided to use it (... I just checked mine as I recently changed the theme, luckily it doesn't do this). It might also be done because people don't care. If it ain't broke don't fix it.
Mostly opinions without data, as usual. Why can't we as a community switch to a more serious scientific/engineeristic approach, at least in those areas where it is easy to do so?

Why should I trust this blog post on the "Caching" point, for example? It's got no data and no references.

"What are the chances that your user has visited a site which uses the exact same CDN as your site?" ... hey, you can measure that.

This depends on the CDN. Lets say you are using google or cloudflare CDN. They have more engineers and better security processes – and work 24X7 -365 days – continuous monitoring – than you remembering to download jquery update. What about if you are on holiday (post/pre COVID era)? BA is not the best example… Rename your article to: Please stop using ‘unknown CDNs’…
Am I the only one that feels using CDNs comes with very little benefit compared to just hosting all resources locally? Almost all security issues go away with locally hosted (i.e., same domain or another domain controlled by the website), plus you avoid an extra DNS lookup, and you still get caching on the same site.

I realize there are benefits, but are the benefits so extreme that they merit all the hype around CDNs? So many developers talk about them like the web would crawl to a halt if they were stopped, but I think that they have their own slippery-slope of problems that has resulted in folks just hand-waving away the expense of web assets since it's a hidden problem from the developer. I doubt actual bytes transferred and latency are affected in a significant way as folk that promote CDNs claim.

It'd be nice to see actual comparisons in a real world scenario. Keep in mind, web site responsiveness is not just linked to download time/size, but also asset processing. If your page is blocking because the JS is still being parsed, the time you saved downloading it is moot.

As a scrub in this domain. When I use a CDN, I get a network that is cheaper per-GB at delivering content, and is better at delivering content. Far more resources in a CDN are configured to deliver content faster and cheaper than whatever rinky-dink EC2 I've got serving the website.
Just how much JavaScript do you have to use for it to make it a significant difference? If you serve 100k of JS (please don't), at 1 million hits per month you're looking at 100GB. Even at EC2's obscene pricing, this is still just $10/month.

If you're that stingy, why aren't you just putting the entire website behind CloudFlare and calling it a day?

(comment deleted)
I have a js file that is over 900kb, (nearing a mb)

Given, its for use in embedded webviews of a video game, and it powers like 50 different atomic interfaces via react.

I wasn't aware that CDNs were much cheaper, and am genuinely curious what service you are using at what prices.

When I look at AWS pricing, us-east-1 at < 10TB, I see: EC2 data transfer out to the internet is 9¢/GB, and Cloudfront is 8.5¢/GB for the lowest price class. That's a slight savings, but at 6% I can't justify the effort to switch over on cost alone.

Should I be looking at a different CDN service?

Cloudflare famously charges $0/GB, with some arbitrary restrictions on the way you use their service, and I’ve heard rumors of soft limits on the total amount of bandwidth you can use before they email you to upgrade to a higher plan.

I’ve never used BunnyCDN, but they charge a flat $0.01/GB for North American traffic, and I’ve heard some good things about them.

DigitalOcean, Vultr, Linode, and some other cloud providers charge $0.01/GB without a CDN, just using their regular servers, but obviously a CDN is more than just a way to save money — it’s a way to lower latency and improve user experience.

The mega clouds (AWS, Azure, and GCP) seem to significantly over-charge for egress bandwidth as a nice profit mechanism, just because they can.

My unpopular opinion is that mega clouds are overrated. They’re fine, but they have a lot of weird gotchas that most people have just accepted as “how the cloud works.”

Remember that the JS CDN thing started 10+ years ago when internet connections were a lot slower and JQuery was the JS framework and only released one new version per year. That means there's a really good chance another website also used it from the Google CDN, and you're browser cached it forever-ish, no DNS lookup needed.

The other thing to remember is old browsers used to cap the number of connections per domain HTTP 1.1 only supports serial requests, so there were benefits to hosting on multiple domains.

Even today, the big benefit of a CDN domain is that CDNs can host static resources faster and cheaper than your webservers. Yes, you can forward requests from the CDN to your webservers, but it's also one point point of failure. What's interesting is that with a modern, JS-only site, the split becomes API and static JS.

Yes, the benefits are substantial. I used to have a static web site served out of a Dallas data center, with a great network connection, from a powerful bare metal machine that did nothing else. Sitting in Austin, it felt instant, equal to localhost speed, until I tried accessing it while traveling, especially overseas.

It's not just geographic latency you're addressing with a CDN, you're also reducing the number of network hops. It's not uncommon to experience higher latency going from SF to San Jose datacenter just because you're on a "wrong" ISP. A good CDN usually has a POP on the same network as you.

I live in Vietnam and frequently give tech advice to people regarding websites they are developing. Most sites on the works fine, but I can tell straight away when someone has hosted their site like you have described - because it will take >5 minutes to load! And they'll be telling me that it's fine, and I have to explain, yes, it's fine if you only want people in the same country as you to access the site. If you want it to work worldwide, you'll have to do better.

Fortunately, these days that just means creating a free Cloudflare account.

Thanks for sharing your perspective.

In your case, are all CDNs equal? Do I just have to throw my content to the biggest provider?

Don't get me wrong. Disenfranchisng non-Western visitors is the last thing I want to do, but the issue is not CDN or not, it's caching content closer to people whose ISPs don't provide sufficient service outside their own borders.

I dislike that CDNs are the only way around this and I feel like it is centralizing Internet access in an unfavorable way.

> In your case, are all CDNs equal?

The big data centers in this region are in Singapore and I expect all CDNs have a presence there, so probably. I haven't exactly done any benchmarking though.

> the issue is not CDN or not, it's caching content closer to people whose ISPs don't provide sufficient service outside their own borders.

How would you do this without a CDN (on a small budget)?

What causes these performance problems? Latency across the globe shouldn't be that bad (200ms or so). Is it limited bandwidth into your country?
There are situations where you do not want or need a global reach. If you are in ecommerce and your not wanting to ship outside your own country then why have images hosted on CDNs?

Scripts and CSS are (or should be) small compared to the images.

Yet, with the images, you can use mod_pagespeed on the server to replace images with picture source sets, with the server able to detect the bandwidth of the client and their device to serve them highly optimised images. So that means images that look glorious on a 4K screen with a good connection and images that are a bit jaggy for the person on their phone with only 3G.

I am sure that CDNs can do this too and that you can get mod pagespeed to work with CDNs but there is so much that can be done on a server without having to sign up for extra services and their overheads.

I believe a site should load all JavaScript files from its own domain if it can. There are problems, but they have solutions too. As an example:

Problem: You can use a home-grown tracking solution, but, if the site has to be sold, how do you provide independent third party verification of your traffic details? Having Google Analytics helps you to do that.

Solution: Install and configure AWStats or Webalizer. Many lower tier hosts have these built into their offerings. Provide those statistics to interested buyers.

Can’t js build tools pack together my code with the third party libraries and then also tree-shake out all the bits I’m not using?