On June 25, 2024, the Sansec security research and malware team announced that a popular JavaScript polyfill project had been taken over by a foreign actor identified as a Chinese-originated company, embedding malicious code in JavaScript assets fetched from their CDN source at: cdn.polyfill.io
. Sansec claims more than 100,000 websites were impacted due to this polyfill attack, including publicly traded companies such as Intuit and others.
What happened with the malicious polyfill library?
Andrew Betts was the original author of the polyfill web service. This project allowed the automatic injection of JavaScript polyfill libraries into websites based on their user agent or other properties. Andrews's statements trace back to February when they warned of having no part in the official cdn.polyfill.io
website.
There is no specific polyfill library on npm that we know is part of this specific malicious actor campaign to inject malicious code. That said, libraries across different software ecosystems, such as Content Management systems like the Magento project and others, might include code that introduces static script imports of JavaScript code sourced from cdn.polyfill.io
. In particular, we have detected CVE-2024-38526, a security report for the pdoc
library on PyPI registry that provides API Documentation for Python Projects. In cases where documentation is generated with the command pdoc --math
would contain links to JavaScript files from polyfill.io
. This behavior of the pdoc
library has been fixed in pdoc version 14.5.1, and we urge users to upgrade as soon as possible.
What is a JavaScript polyfill?
A JavaScript Polyfill is often a purposely built piece of code that provides modern functionality on older browsers that do not natively support it. Historically, polyfills were crucial for web developers aiming to create applications that work seamlessly across different browser versions. They act as a bridge, enabling older browsers to run newer JavaScript features, ensuring a consistent user experience regardless of the browser's age or capabilities.
In the early days of web development, browsers evolved at different paces, leading to a fragmented environment where the same code might not run uniformly across all platforms. Generally speaking, support for browser APIs would not be equally supported. With no control over browser versions available to end-users, developers couldn’t guarantee the same API available when JavaScript code executed in browsers. As such, polyfills emerged as a solution to this problem by introducing polyfill libraries, allowing developers to write modern JavaScript code without worrying about compatibility issues. For example, methods like Array.prototype.includes
and Promise
were not supported in older browsers like Internet Explorer. Still, developers could make these features available with a polyfill library loaded in the browser.
The role of a JavaScript CDN in polyfill libraries
A Content Delivery Network (CDN) is a system of globally deployed and distributed servers that deliver web content to users based on their geographic location. In the context of JavaScript polyfill libraries, CDNs play a vital role by hosting and serving these libraries efficiently across the globe. By leveraging CDNs, developers ensure that their polyfills are delivered quickly and reliably to users, minimizing latency and improving load times. Using a CDN was also helpful to developers to avoid the need to bundle JavaScript libraries.
A general use case for a CDN that you are likely to encounter is the use of cloud-based metrics and application performance, such as Google Analytics, which officially proposes that you add the following code to your website:
{
"vars" : {
"gtag\_id": "<GA\_MEASUREMENT\_ID>",
"config" : {
"<GA\_MEASUREMENT\_ID>": { "groups": "default" }
}
}
}
In the case of the malicious polyfill takeover, cdn.polyfill.io
was a widely used CDN that dynamically served polyfills based on the HTTP headers of incoming requests. This meant that the appropriate polyfill was delivered based on the user's browser and version, ensuring optimal compatibility.
Security risks of polyfills hosted on a CDN
Using polyfills hosted on a CDN introduces significant security risks, primarily due to the potential for arbitrary JavaScript code execution within the application context. This risk is often reported as a Cross-site Scripting (XSS) vulnerability for a given web application.
When a polyfill library is fetched from a CDN, the application relies on the integrity and security of the external server. As in the CDN source in itself. If the CDN or the hosted library is compromised, as seen in the recent attack on cdn.polyfill.io
, the newly compromised code can be injected and executed within the user's browser. Such malicious code can perform various nefarious activities, such as redirecting users to phishing sites, stealing sensitive information, or even further propagating malware. When it comes to browser security, this sort of XSS vulnerability is the most severe consequence.
How could Snyk detect vulnerable JavaScript libraries on a CDN?
Beyond detecting insecure code and vulnerable third-party libraries in your project manifest and project dependencies, the Snyk VS Code extension also supports detecting vulnerable libraries that were imported using static script import statements.
For example, if the lodash
library imported from a CDN were to use a vulnerable version range or was known to include malicious code, Snyk will append in-line annotations to drive the developer’s attention to the security risk.
Protecting against CDN supply chain attacks
The recent attack on the JavaScript polyfill project highlights the critical importance of supporting resources across the web ecosystem, and CDNs are a significant part of that. Supply chain security concerns often revolve around open source package registries such as PyPI and npm but the JavaScript polyfill attack reminded us that CDNs are also an incredible building block of the web.
Following are some best practices you should consider to help protect from such attacks:
- Use trusted CDNs: Only use CDNs from reputable providers. For example, Cloudflare is well-known for its robust security measures and reliability.
- Monitor dependencies: Regularly audit and monitor all third-party scripts and dependencies.
- Subresource integrity: Tools like Subresource Integrity (SRI) can help ensure that the content delivered by a CDN has not been tampered with and can be pinned to an expected version/hash that has been audited and known to be clear of malicious or otherwise undesired behavior.
- Content Security Policy (CSP): Implement a strong CSP to restrict the sources from which scripts can be loaded. This can prevent malicious scripts from being executed. Since polyfills are often included in the critical path of application loading, they run with the same permissions as any other JavaScript on the page, making them a prime target for attackers aiming to exploit this trust. This risk underscores the importance of using secure and reputable CDNs, implementing robust security measures like Content Security Policy (CSP), and regularly auditing third-party dependencies to safeguard against such vulnerabilities.
- Regular updates: Keep all libraries and dependencies up-to-date. Many attacks exploit known vulnerabilities that have been patched in later versions.
- Alternative solutions: Evaluate whether polyfills are still necessary for your project. As browsers modernize, many features provided by polyfills are now natively supported. Highly consider vendoring your dependencies with your own project assets instead of reliance on third-party providers such as CDNs.