45

My website pages reference some JavaScript code from a third-party CDN (analytics, etc). So I don't control what code is there - the third party may change those scripts at any moment and introduce something bad into those scripts - maybe accidentally, maybe deliberately. Once those scripts are changed, the users start receiving and running new code in their browsers.

What risks are raised by this uncontrolled code? What's the worst thing that can happen? How do I get started with managing the risk?

M'vy
  • 13,033
  • 3
  • 47
  • 69
sharptooth
  • 2,161
  • 1
  • 19
  • 22
  • 1
    This is a bit broad - the JS code you include can do almost anything that JS code you wrote onto the page can, with a handful of exceptions resulting from the same-origin policy. These often come down to "what can my Javascript do?", which depends on your specific app. – Matthew Feb 18 '16 at 13:53
  • 3
    The worst thing that can happen is that your customers have malicious code injected into their pages, and they get hacked/tracked/etc. It always comes down to your customers/visitors. – Mark Buffalo Feb 18 '16 at 14:04
  • 39
    If you run someone else's JavaScript on your site, it ain't your site no more. – AviD Feb 18 '16 at 14:07
  • 6
    @AviD true to a point but I'm not sure I've ever heard of any site that doesn't have some third party javascript on it. Google Analytics is practically everywhere as an example. If not that then adsense. If not that then some alternative to those. – trallgorm Feb 18 '16 at 14:22
  • 8
    Perhaps I am too lenient, but I am voting to keep this post open. Basic security concepts are often lost on many people, and I don't think it's fair to assume everyone is going to know everything. Plus, it offers somewhat of a guideline to those starting out, and a warning about what could happen. – Mark Buffalo Feb 18 '16 at 14:37
  • I am not personally familiar with the registration and approval process for getting JS files onto a CDN, be it Google or MaxCDN, but I would imagine that **IF** https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js started serving a malicious file then you would read or hear something about it sooner rather than later. Definitely avoid sites like https://shady.cdnguy.com/jquery.1.12.0.min.js – MonkeyZeus Feb 18 '16 at 16:00
  • 2
    @AviD If you use CDN, it ain't your site no more just as well. If a CDN can't be trusted, it shouldn't be used at all. – Dmitry Grigoryev Feb 18 '16 at 16:05
  • 9
    Related: [Forbes says no to adblockers...](https://www.techdirt.com/articles/20160111/05574633295/forbes-site-after-begging-you-turn-off-adblocker-serves-up-steaming-pile-malware-ads.shtml), and then serves up Malware... Why is it related? because third party software (in this case, advertisements) got hijacked to server malware (password stealing). I highly doubt Forbes did it on purpose, but it happened none the less. – WernerCD Feb 18 '16 at 19:27

5 Answers5

53

Risk

In the worst scenario, it could render website completely inaccessible for the users, it could perform particular actions as them (for example, requesting account removal, spending money) or it could steal confidential data.

Prevention

Can't be done. If you run someone else's JavaScript on your website, it becomes no more secure than that third-party. You have to host it yourself.

You can get closer to the target, however.

  1. One thing may be Content Security Policy. The example setting of header Content-Security-Policy to script-src 'self' www.google-analytics.com;, would prevent execution of scripts served from other domains than your own or www.google-analytics.com. That way if someone would find some cross-site scripting vulnerability (XSS) that would allow them to add their own inline JavaScript code to your website - it would not run.
  2. Other really cool thing is so called Subresource Integrity. It's essentially adding hash sum of JS you expect to run to the integrity parameter you give to the script tag.
<script src="https://example.com/example-framework.js"
    integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
    crossorigin="anonymous"/>

You can generate those hashes online at https://www.srihash.org/ or with command:

openssl dgst -sha384 -binary FILENAME.js | openssl base64 -A 

This of course has downsides, e.g. analytics providers may change their scripts and you will have to change integrity parameter to keep them running. It's also pretty new feature (Chrome 45.0+, Firefox 43+, Opera 32+, no IE, Edge or Safari support at the moment), so you lay your client's security on their own software.

See also 3rd Party Javascript Management Cheat Sheet from OWASP.

Przemek
  • 591
  • 3
  • 10
  • I thought about csp as well, but removed it from my answer, because I don't think it's a viable solution. The OP is worried that the external script (eg at `google-analytics.com`) is malicious, so your example wouldn't help. It would also not help in the case of most XSS, as inline js comes from `self` and would thus run (it makes XSS a bit more obvious as the whole payload must be included though). csp also does not really prevent the sending of data to external servers (for that to work, all requests must be forbidden, including things like images, which restricts usability). – tim Feb 18 '16 at 14:38
  • It wouldn't help to prevent execution of script from the source that OP intends to include, but it would prevent, as OP called it in the title of this question, "some random out of [my] control javascript". I don't agree on inline JS, however. To run that, you would have to add `unsafe-inline` to your CSP. Without it, inline JS won't run. – Przemek Feb 18 '16 at 14:45
  • You are right about inline js. Still, I don't think that it solves the OPs problem. And most websites have at least some inline js, so in most cases, it's not a viable solution to XSS either. – tim Feb 18 '16 at 14:51
  • If google's javascript were to become malicious, *or be intercepted through a man-in-the-middle attack*, then that's bad-news bears. – Mark Buffalo Feb 18 '16 at 14:51
  • @MarkBuffalo I don't think so. If the script becomes malicious, it still comes from `google-analytics.com`, which is allowed by the csp. And a man-in-the-middle doesn't change the origin either, it would still appear to come from `google-analytics.com`, which is still allowed by the csp and would thus execute. – tim Feb 18 '16 at 14:54
  • @tim: but if it's contents changes, the integrity hash won't match anymore and script won't execute. And CSP kicks in for other cases that I think OP intended to know about too (title suggests that, for example). Regarding inline-js, one word: nonce. – Przemek Feb 18 '16 at 15:00
  • Couldn't prevent be done by copying the JS libraries you depend on to your server? That way you don't pull your libraries from some CDN that can change from under your feet, instead you pull from your own hardware that you control. – David says Reinstate Monica Feb 18 '16 at 16:05
  • @DavidGrinberg: You are absolutely right, that's what I said in my first paragraph of "Prevention" section. However, sometimes you just have to use CDN or other third-party scripts and this is when the said methods come in handy. – Przemek Feb 18 '16 at 16:17
  • @Przemek Oh, missed that line, my bad. – David says Reinstate Monica Feb 18 '16 at 16:24
  • +1 for hosting it yourself. If a third-party has an interesting script you want to run, you're most often allowed to run it from your own server as well. This will eliminate the risk of the code suddenly changing into something unspecified. – Mast Feb 18 '16 at 22:15
  • I didnt know about the  Subresource Integrity, thanks – rpax Feb 19 '16 at 06:46
  • *you lay your client's security on their own software* ...isn't this always the case? – N.I. Feb 19 '16 at 12:31
  • @NajibIdrissi: Well, you can introduce many protections server-side, making security independent from client. With my _you lay your client's security on their own software_ I tried to say that Subresource Integrity is not a method of ensuring you don't serve something malicious, but only way to tell modern browsers what they should allow to be served. – Przemek Feb 19 '16 at 13:04
  • My point is that whatever you do, ultimately it will be the client's software that will be part of the security... It's all well and good to use HTTPS for example, but what if your client uses some crazy browser that decides to broadcast every form submitted in clear text to everyone, regardless of security settings? You can't do anything to prevent that, and you can't do anything to force a browser to respect the integrity check... – N.I. Feb 19 '16 at 13:06
  • @NajibIdrissi: You are absolutely right, you will never be able to protect user if they won't want to protect themselves. With that _you lay your client's security on their own software_ I only tried to show question's author that Subresource Integrity won't help him not serve the malicious code, it only may secure users from running it. – Przemek Feb 19 '16 at 13:29
  • If yoou provide a hash, there's still a little chance that the script is changed: hash collisions... – wb9688 Feb 20 '16 at 17:00
  • @wb9688: Subresource Integrity currently allows sha256, sha384, and sha512 hashes. There are no known collisions for any of those. – Przemek Feb 20 '16 at 17:36
8

What's the worst thing that can happen?

The third party can do anything that you could do with JavaScript - or anything an attacker could do with XSS.

This includes stealing cookies, injecting a JavaScript keylogger, bypassing CSRF protection and thus executing any request you could execute - eg adding a new admin user -, or changing the content of your webpage (eg to inject ads or for phishing attacks).

How do I get started with dealing with the risk?

If you do not trust the intentions or security of the third party, the only proper solution would be to host the script yourself (after you made sure that it does only what you want it to do).

If you do not want to do that, you could add Subresource Integrity to somewhat mitigate the risk:

You add a hash of the script to the include and modern browsers will compare the hash to the hash of the included file. If it doesn't match, the code will not be executed: <script src="https://example.com/script.js" integrity="[hash]" crossorigin="anonymous"></script>. This way, you can make sure that the script will not change. Of course, if it does change, you need to re-check the script and change the hash, as otherwise your website will be broken.

tim
  • 29,018
  • 7
  • 95
  • 119
6

What risks are raised by this uncontrolled code? What's the worst thing that can happen?

Generally, the attacker gains access to anything your authentication methods are meant to protect.

Worst case, assume the code is custom designed for your site. Actions the user takes can be prompted to input security credentials, giving the attacker access to that user's account long after they log out. For a bank account, a bitcoin wallet, or other sites controlling access to assets the attacker will have access to irreversible, high value actions. If there isn't value at the time but they get the credentials to access the account, they can wait until there is enough value in the account to make it worth while.

How do I get started with dealing with the risk?

Good: Set Content Security Policy and Subresource-Integrity on the scripts you host yourself. See @Przemek's answer for more information about those. Not sure if this needs to be said, but all JavaScript should be served over HTTPs; this at least raises the cost of MITM for those resources.

Better: Host the code yourself. But, don't forget to update it regularly. These vendors release security fixes of their own. Don't get left behind.

Best: Review all the source code. Most of the time you can find un-minified versions of the JS which you can read yourself(and eventually just diffs), and then minify and bundle it up with the rest of your sites JS. Obviously this is high cost, weight it appropriately against your site's needs.

Steve Ellis
  • 215
  • 1
  • 4
1

What's the worst thing that can happen?

The technical term is arbitrary code execution. In this context, "arbitrary" means "any," as in "anything that it's possible to code in JavaScript could end up running on your site."

Is it possible to use JavaScript to read values from form fields? Yes. Is it possible to use JavaScript to load something from a URL? Yes. Then arbitrary code execution means someone could read a user's username and password as they log in, and transmit them to their own server, encoded as parameters in a GET request. (This provides a neat end-run around HTTPS encryption of your users' login data, BTW.)

Is it possible to use JavaScript to read data on a page? Yes. Then arbitrary code execution means that someone could read a user's private profile data when they access the page that contains it. (And use a HTTP request to report it back to the attacker's server.)

...and so on. An arbitrary code execution vulnerability is considered the worst thing possible from a cybersecurity perspective, because exploiting it means that literally any bad thing that is possible can be performed on your system.

Mason Wheeler
  • 1,625
  • 1
  • 11
  • 15
1

All of the above are worth considering, but to be fair, there are well-accepted development practices as well as built-in protections in modern browsers that protect against the most severe cases noted.

It's also worth noting that there are three ways that third-party code can run in the context (that is, sharing the same browser DOM) as your web pages: 1) JS that you include and render along with your page, e.g. Google Analytics, or 2) Bookmarklets that the user controls (e.g. Spritzlet or Pinterest), and 3) browser extensions or toolbars. The latter two are almost completely out of your control; the first is something you can audit to some degree.

By far the most important thing to ensure is that the web servers you do control that serve and respond to the client (browser, bot, malware) are locked down. XSS, CSRF, SQL Injection and multiple other attack vectors are within your control on the server side. You would have to explicitly allow CORS on your server side, but if you do, make sure you know what you're doing, and be particularly vigilant. This is not to say this stuff is all easy or obvious or anything, but it's entirely independent of whether it the vulnerability is breached via JS or any other method.

Assuming you have protected your web server and locked down the endpoints that can be called, the rest of what goes wrong falls into a different class. JS (and plugins/extensions/toolbars/bookmarklets) can do a wide range of bad things -- keystroke loggers, injection of harmless looking elements that actually send data elsewhere, and so on. All of these are executed by the browser.

If you're serving the JS on behalf of a third party, you should be careful to trust and verify the source. A Google Analytics snippet is probably safe. A third-party ad-serving widget might be worth looking into more carefully. In all cases, the code behind these can be inspected: if the browser can run it, you can see the JS code and decide: is this something you want on your site?

JavaScript is a powerful tool. But in the end, JS is software that the browser execute and so we put a lot of faith in browsers and operating systems that run them to ensure safety. JS is not software that has particular magical abilities to breach your server or make your server do stuff it's not designed to do.

There's little to nothing your site can do that allows JS to arbitrarily execute code on a the user's computer -- it is the user that must have recent updates to browsers and OS, etc. You could detect old versions and post warnings to be a nice guy, but that's about it.

Secure your server, install updates, make sure your code is safe, avoid serving JS from unknown third parties. And then, make sure your site is registered with Google Webmaster Tools which will notify you if your site is hacked in many cases, and if you can afford it, get a service that scans your site for vulnerabilities.