This past February the internet became acutely aware of the dangers of trusting third party libraries when the popular Browsealoud service was compromised and distributed a malicious cryptominer. Browsealoud provides screen reading (text-to-speech) and other web services to enhance the accessibility of websites and is used extensively by government organizations to meet accessibility requirements. 

Websites using the Browsealoud libraries were infected with a malicious cryptominer known as Coinhive, a program that allows users to ‘mine’ Monero a cryptocurrency similar to Bitcoin. The malware stole the processing power of the user’s device to then mine cryptocurrencies.

So, when it was discovered that the malicious cryptominer had been injected into over 5000 websites via trusted Browsealoud JavaScript, the impact was clear. Unfortunately, the solution was not.

Although the use of third party libraries and services is ubiquitous in today’s cloud hosted world, the explicit trust given to third parties always increases an application’s attack surface.


JavaScript sourced from third party content delivery networks (CDN) has complete access to the websites Document Object Model (DOM) and can manipulate the contents of web pages, access sensitive user data, or as it turned out in this case, occupy the CPU and mine cryptocurrencies.

 

Although the nature of the attack is subtly different, the impacts of a compromised CDN are identical to any Cross-Site Scripting (XSS) Vulnerability.

 This is why security researchers place such an emphasis on ensuring that TLS is enabled for all resources, not just the content served by the primary domain. Loading a single script over unencrypted HTTP makes a website vulnerable to man-in-the-middle (MitM) attacks. However, just because a resource was loaded over HTTPS does not prove that it has not been tampered with, it only protects against tampering during transit. The script could have been maliciously altered prior to transit, as was the case in the Browsealoud story.

The straightforward and most secure option to protect against a compromised CDN unknowingly serving malicious content is to simply host the script yourself. Although you don’t get the distributed caching benefits, web browsers still cache static content after the first load, so the performance penalty may be acceptable given the improved security posture. Since integrating the Browsealoud services into your website is a simple matter of sourcing a single script file (ba.js), one might conclude that this is a reasonable option. Unfortunately, the first line of the ba.js script warns against this.

/* [Warning] Do not copy or self host this file, you will not be supported */

/* Browsealoud Plus v2.5.0 (13-09-2017) */

BA-Launchpad-Icons-email-01_2

 


SUBRESOURCE INTEGRITY (SRI)

Over the past couple of years, a new option has emerged which could effectively mitigate selected attack vectors by ensuring the integrity of externally sourced libraries.

Subresource Integrity (SRI) is a web browser feature that was proposed by the W3C to validate assets provided by CDNs. Websites can specify the cryptographic hash of a given third-party resource in the integrity attribute of any associated script and link tags.

An example configuration looks like:

 <script src="https://example.com/example-framework.js"

integrity="sha384-eW91J3JlIGVudGlyZWx5IGJvbmtlcnM=” crossorigin="anonymous"></script>

Chrome, Firefox, and Opera have supported SRI since April 2016 but, unfortunately, Microsoft (IE, Edge) does not support for SRI at the present time. Consequently, self hosting third party  libraries remains the most secure option until there is wider adoption.

Nevertheless, SRI has many virtues and validating the integrity of external resources will be the way to go in the near future. For web applications that source a handful of static JavaScript libraries or stylesheets, such as jQuery or Bootstrap, SRI is an excellent solution that provides strong integrity controls while  continuing to enable the benefits of CDN hosted content.

As Troy Hunt pointed out, the Browsealoud scripts are not a versioned, static, resource. This is why they warn that you shouldn’t host the ba.js script yourself. It is software as a service, it is meant to change. So there are no guarantees that it’s hash will be constant. This is not the scenario SRI was designed to protect against.

Furthermore, even if ba.js were a statically versioned resource, SRI would still be an insufficient security protection. This is where other security analysts discontinued their research, but there is more to the story that needs to be told.

 

Browsealoud  

To use the Browsealoud service, a website embeds the ba.js script in their page which, when invoked, provides the user with text-to-speech accessibility features in the form of a floating toolbar.

<script src="https://www.Browsealoud.com/plus/scripts/ba.js" type="text/javascript"></script>

As the ba.js script executes, it dynamically loads numerous additional JavaScript libraries and resources.

An abbreviated list of the dependencies, scripts and resources loaded by ba.js follows:

https://plus.Browsealoud.com/modules/2.5.1/ba-library.min.js

https://plus.Browsealoud.com/modules/2.5.1/ba-library-ui.min.js

https://plus.Browsealoud.com/js/urlinfo/www.example.gov.js

https://plus.Browsealoud.com/modules/2.5.1/Browsealoud.min.js

https://plus.Browsealoud.com/js/locales/1.min.js?v=2.5.1

https://babm.texthelp.com/Pronunciations.ashx

https://plus.Browsealoud.com/modules/2.5.1/simplify/body.html

https://plus.Browsealoud.com/modules/2.5.1/settings/body.html

https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVZ0b.woff2

https://baspeech.speechstream.net/SpeechServices/index.html

 


At least a few of the resources loaded at runtime contain dynamic content and will not have a stable cryptographic hash. In particular, the JavaScript library (www.example.gov.js) contains client specific configuration content including an expiration timestamp (expireydate) and a URL identifier (UrlId). However, even the static content cannot be protected by SRI because it does not have a way to configure the integrity attribute for content that is dynamically loaded at runtime. There are also no guarantees that this list is complete since content is loaded dynamically based on user behavior.

Content Security Policy (CSP)

This is where Content Security Policy (CSP) plays an important role. By itself, SRI provides the ability to selectively validate the integrity of individual scripts. However, scripts that are loaded without the integrity attribute set will execute, as normal, without integrity controls. Thankfully, CSP can prevent this. By default, CSP instructs the browser to only allow execution of unobtrusive scripts from the same origin. CSP disallows the execution of scripts from other sources including inline script tags, dynamically loaded scripts, and content that would be executed by dangerous functions such as eval().

CSP also supports configuration options that allow third-party scripts, so long as their integrity is protected by SRI or marked with a nonce. To enable execution of third-party scripts and stylesheets that are protected by SRI, configure CSP with the "require-sri-for script style” directive. SRI and CSP are complementary technologies that can be used together to prevent execution of externally hosted content unless it’s hash is specifically whitelisted and integrity verified. 

In context with CDN hosted static libraries, this is a very secure configuration. Unfortunately, this won't work for the Browsealoud service because it loads scripts at runtime. From a security perspective, this is an excellent example of how CSP enhances the security of your site by preventing the execution of dynamically loaded content. However, if you have accessibility requirements and rely on the Browsealoud for text-to-speech services, further modifications to the CSP policy must be made.


The 'strict-dynamic' CSP directive can be used to allow execution of dynamically loaded scripts, so long as they were loaded from an explicitly trusted source such as a script which is protected with SRI. Combined with the ‘require-sri-for’ directive, it is possible to selectively permit resources, and all subsequent dynamic content, without allowing arbitrary execution from unknown sources. For sites with numerous CDN hosted scripts and dependencies, the strict-dynamic directive is easier to manage than lengthy source whitelists. An example policy looks like:

Content-Security-Policy: script-src 'require-sri-for script style' 'strict-dynamic'

Although the permissive CSP policy described above is certainly more secure than using SRI by itself, it does not mitigate all security concerns. For one thing, this configuration does not prevent attacks against the dynamic content. Even if the integrity of ba.js were validated with SRI and external scripts were disallowed by CSP except those loaded by ba.js, the attacker could simply inject malicious code into a dynamic resource. As a result, protecting against a compromised service like Browsealoud is significantly harder than one might expect.

Dynamic Audio & Simplified HTML

The analysis of the Browsealoud compromise would not be complete without also considering the attack vectors presented by resource types other than JavaScript, such as audio streams, fonts, and HTML. As a text-to-speech service, Browsealoud provides a mechanism for the user to select text on the web page and convert it into an audio stream. To accomplish this, the Browsealoud scripts POST the selected text to services at baspeech.speechstream.net, which returns the URL to a dynamically generated MP3 media file and corresponding XML metadata file.

POST /SpeechServices/index.html HTTP/1.1

Host: baspeech.speechstream.net

Content-Type: application/x-www-form-urlencoded;charset=UTF-8

text=hello%20world&userName=Browsealoud&voiceName=Vocalizer%20Expressive%20Ava%20Premium%20High%2022kHz&speedValue=40&...

HTTP/1.1 200 200

Access-Control-Allow-Origin: *

Cache-Control: no-cache, no-store, must-revalidate, max-age=0

Content-Type: text/plain

server=amazon&xml=https://generator_35-178-93-36.speechstream.net/dynamic/a/3/998cfc248098c4b541592e76ff442ea3.xml&mp3=https://generator_35-178-93-36.speechstream.net/dynamic/a/3/998cfc248098c4b541592e76ff442ea3.mp3


Because dynamic web service content, such as audio streams, cannot be protected by CSP or SRI, the web application must explicitly trust the security of the third party. In the event the the service is compromised, attackers could modify the response to point to a different MP3 stream and inject arbitrary audio into all client browsers. This would not be the first appearance of Rick Astley to the security stage. The potential impact of this threat poses risks that are perhaps greater than a malicious cryptominer.

Unfortunately the difficulty securing the Browsealoud service does not stop here. Browsealoud also provides a handy feature to simplify a webpage to make it more  accessible. By clicking on the “simplify” button, the HTML of the page body is sent within a POST request to babm.texthelp.com, analyzed and a simpler plaintext version is returned as a JSON object. If this third-party host was compromised, arbitrary HTML or script tags could be included in the response, defacing the page or resulting in an XSS style code execution vulnerability.

SRI and CSP are admirable technologies that can protect users against insecure CDNs when dealing with static libraries. However, when software is provided as a service and content is dynamically generated and loaded at runtime, such as is the case with Browsealoud, SRI and CSP are not enough. Ultimately, applications that rely on software a service, must explicitly trust the provider and accept the inherited risk.