In our previous blog post we discussed how client-side code — code residing in a web application — has become the largest part of a web app, and a popular method for developers to use when they introduce new capabilities into web applications. We touched on how unauthorized modification of client-side code has become a popular method for changing the content or altering the behavior of websites through malicious attacks. We also covered why website operators have no visibility into what happens inside their users’ browsers when their client-side code is changed. We explained briefly how JavaScript code that runs on a website may actually be generated elsewhere in a JavaScript library that is controlled by a third-party vendor or open-source maintainer. And we looked at how these unauthorized changes to otherwise respected and trusted JavaScript libraries run on the client side, through injection of malicious code, are an increasingly popular attack method against e-commerce, travel and finance company web applications.
In this post, we’ll go deeper into the problem. More specifically, we will review how changes to JavaScript libraries made in client-side attacks can alter website behavior without site visitors noticing. We will explore how these types of behaviors can harm end users. These damages include manipulating the user experience, hijacking the user’s session and leaking or stealing user data such as sensitive personal information, account passwords, and financial data like bank account or credit card account numbers. Additionally, this post will also review some of the known recent client-side attacks on JavaScript libraries. We will conclude by covering common approaches to face these threats, what the problems with those approaches might be, and recommend best practices to deal with these threats.
JavaScript Methods Used by Bad Actors
Before running through real-life threats and examples, let's review the power JavaScript code has to control and modify what a user sees or experiences in a web application. On every web page, JavaScript code can access, modify, create an alternative for and remove anything from the page. This includes UI elements, object prototypes, storage assets, and network activity. Large portions of website functionality are being executed via JavaScript libraries - snippets of pre-written JavaScript that make it faster and easier to do things. This is a tremendous amount of power. Users are at high risk if a website they are visiting has a breached library or code, which is magnified by the fact that there are no easy ways for either site operators or site visitors to know when such changes have been made.
Let's review a few of the common javascript vulnerabilities used by hackers when they breach client-side code —typically via a JavaScript library — to manipulate the application and steal or skim data.
DOM Modification
DOM stands for Document Object Model, which is, according to Mozilla, “...the data representation of the objects that comprise the structure and content of a document on the web.” In other words, the DOM is a key component representing a webpage and the relationship between all of its elements. Malicious DOM modification is any JavaScript code that modifies the webpage by introducing new code that manipulates the DOM. Malicious JavaScript libraries can use this method to execute pieces of code that will present fake content, display unauthorized ads, fabricate a form asking the user to provide their social security number or bank account, and otherwise alter the intended visual experience or functionality of a website from the way it was originally designed by the website operator.
Browser Storage Data Access
Modern browsers support several types of web storage. The most common types are cookies, session storage and local storage. Each storage type serves a different purpose and has a different scope. What's common to the entities above is that they usually contain sensitive user data. JavaScript running on a webpage has access to nearly all types of storage. What’s more, JavaScript can read and change storage key values. In doing so, it can access or modify personally identifiable information (PII), social network tokens, affiliation codes, session keys, user histories, clickstreams and more.
Network Sniffing and Manipulation
JavaScript code can generate network calls in all kinds of protocols, but it also has the power to monkey patch network activity — i.e., extend or modify supporting system software locally. By that, it can modify network calls parameters, content, headers and target domains. It can also replay the same network request by cloning its entire content and modify the target. This can be used to gain unauthorized access to network capabilities to make a browser or a web application appear to be something it is not or fake a user identity.
Data Harvesting
Malicious scripts can monitor browser events, form input changes and user interaction. These scripts have the ability to observe, collect and report any kind of data associated with the web page. Usually, this data is captured and exported to an unknown location and an unauthorized server or service using different types of network protocols suggested by the browser. From payment information to personal information, everything is at risk and could be stolen.
Real Life Threats and Stories
Let’s review some popular and commonly used threats that have appeared in the last few years. These attacks leverage many of the powers and weaknesses that we detailed about JavaScript in the previous section of this post.
Digital Skimming and Magecart
The idea behind digital skimming is simple: by secretly modifying legitimate JavaScript libraries with the malicious code, the attacker monitors payment transactions, harvests credit card numbers, user IDs and other key payment details, and sends them to a designated gateway to be logged. The common user can’t tell their information was leaked because the original transaction is processed as normal. The website operator, likewise, is unaware that their user’s data was stolen.
Magecart is the most prominent example of digital skimming and started initially targeting online stores powered by the Magento platform in 2016. Since then, it has spread to many other types of commerce platforms. Magecart attacks follow a familiar pattern. Criminal hackers breach the store’s back-end, using unknown or known but unpatched vulnerabilities in the software or in plug-ins used for many eCommerce applications. The hacker then alters the site’s code, directing it to load JavaScript that would monitor and record activity on sensitive pages where payment information is taken from a customer. The rogue JavaScript would send the payment data to a remote server controlled by malicious hackers.
Example: In January 2022, Segway fell victim to a Magecart attack that allowed cybercriminals to access customers’ credit card information. Attackers embedded malicious code in an icon file for displaying Segway’s logo on users’ browser. The file wasn’t inherently malicious itself, but dynamically loaded the skimmer in users’ browsers — remaining invisible to anyone examining the HTML source code. And because the logo was still rendered correctly, the malicious code was not apparent externally either. It was estimated that the skimmer had been active for several weeks before it was discovered, exposing the data of customers in the United States, Australia, Canada, the UK and Germany. By debugging the skimmer’s loader, researchers revealed its command-and-control (C2) URL, booctstrap[.]com, which is a known skimmer domain that had been active for several months.
PII Harvesting and Formjacking
PII harvesting is when cybercriminals inject malicious scripts to manipulate your website forms. Also called formjacking, this allows the attacker to collect personally identifiable information (PII) from your users when they submit a form, usually on a login or checkout page. The PII data may then be sold on the dark web and or used in subsequent attacks such as credential stuffing, carding, and account takeover (ATO). These attacks take place on the client side, so they often aren’t caught by code scanners and web application firewalls (WAFs).Because it’s difficult to get visibility into how code is behaving in users’ browsers, these PII harvesting can go undetected.
Example: Cybercriminals exploited a cloud video platform to launch a formjacking attack on around 100 real estate sites in January 2022. Researchers found that the attackers injected malicious JavaScript code into the video, so when users imported the video, their websites were embedded with the skimmer. This allowed the cybercriminals to capture PII from the sites’ HTML forms when users submitted them.
Session and Credential Hijacking
Session hijacking is a technique used to steal users’ sessions in an ‘auth’ protected protocol — like OAuth — and bypass authentication procedures. JavaScript accesses users’ storage assets in the browser or web application, where social network tokens and session credentials are usually kept. In this manner, a rogue JavaScript library or code injection can hijack users’ sessions and authenticate on their behalf using these stolen credentials.
Examples: There are numerous examples of these attack types, which can include stealing everything from info stored in cookies to PII to other useful forms of financial or personal data. In one such incident, criminal hackers were grabbing Facebook user data via third-party JavaScript trackers included on sites and web applications that use the “Login With Facebook” for user signup and authentication. According to those reports, the exploit lets the trackers skim some or all of a user’s PII data provided to the site. This PII data could include name, email address, gender, age range, location and profile photo. What the trackers were actually doing with the data was not obvious, but it is known that the parent companies behind the trackers sell services to publishers to help them make more money based on data from users.
The above examples cover just a small portion of threats resulting from breached JavaScript libraries. As we mentioned previously in this post, the risk to users and site operators is high due to the easy accessibility and enormous power of JavaScript running in the browser. The bottom line here is simple and disturbing: many web applications are not safe. Due to the power JavaScript has in the browser and web applications and the stark lack of security around JavaScript, users are often exposed to client-side attacks that allow cybercriminals to steal their data. Today’s browsers are not designed to protect end users from JavaScript attacks. With a recent increase in client-side attacks, there is a dire need to improve our methods and tools for protecting against JavaScript attacks running on the client-side.
How To Protect Your Web App
While your gut reaction may be to enforce rigid standards or completely stop using client-side JavaScript, a true lock-down security of the browser and web applications would have significant costs. In the past decade — and even more quickly in the past five years — web development standards have changed. We develop faster, using multiple teams in different regions to build websites and web applications.
Frequently, JavaScript code is added by multiple, and often non-technical, parties in the organization. No one wants to slow down the pace of development and innovation. Striking the ideal balance between flexible, fast and highly collaborative and distributed development methodologies versus improved security is always a challenge and a trade-off. That said, due to the severity of threats facing client-side security and the rising tide of JavaScript attacks, it’s pretty clear we need to consider some broad changes and massively improve the security around web applications development, deployment and operations.
There is no silver bullet to protect against all client-side attacks, but here are a few options to significantly reduce your risk:
Content Security Policy
Content Security Policy (CSP) is a secure HTTP-based layer that is built into modern browsers. Using CSP, a web developer can apply content rules on allowed assets. This allows for highly granular control of what content can and cannot be modified by Javascript when a web application loads. CSP is managed by static tags which are not easy to update or programmatically modify. As a result, CSP requires a full redeployment of the entire web app whenever a new policy is applied. While CSP will block any JavaScript code that does not meet its rules, it can also block unforeseen edge cases. Using CSP to protect application content is a good idea but the tradeoff is that it leads to a longer and more inefficient development process. And if CSP is used improperly, it can significantly slow down your web app, impacting the user experience.
Browser-based JavaScript Blocking
Granular JavaScript blocking allows security teams to block specific actions without disabling the entire script. This means you can prevent JavaScript from accessing sensitive PII and PCI data fields, like credit card information and SSN, while still permitting it to collect information from approved data fields. JavaScript blocking offers control over browser-based JavaScript code, so you can retain functionalities while still enforcing PII and PCI compliance.
SAST and External Scanners
Static Application Security Testing (SAST) is a technique for examining an application’s source code before it is run. You can also use external scanners to analyze scripts in a sandbox environment to detect malicious behavior. These methods allow you to proactively identify potential code vulnerabilities and get a snapshot of how client-side code is behaving. However, malicious scripts have gotten smarter; many will only load in a real client-side environment to avoid detection by SAST and scanner. Furthermore, scripts that load dynamically in browsers will remain undetected. Even if an external scanner does find malicious activity, all they can do is issue an alert; they cannot actually block it.
The Right Approach
Manual code analysis and static scanning won’t give you complete visibility into client-side code that loads dynamically. For that, you need a security solution that delivers continuous insights into the behavior of first-, third, and nth-party code in users’ browsers. This gives you real-time visibility into scripts that are accessing sensitive fields and exfiltrating the data in real-time, as well as code with known vulnerabilities.
Now, it’s time to mitigate. I recommend a combination of content security policy (CSP) and granular browser-based JavaScript blocking. CSP stops malicious scripts from loading and prevents data transfer. Javascript-blocking gets more granular, disallowing scripts from accessing certain sensitive data fields without disabling them entirely.
This type of capability was not possible until recently due to the limitations on real-time monitoring at the global scale and the resource and latency requirements of machine learning algorithms that must drive this technology. However, it is increasingly possible for website owners to keep up with the dynamic lifecycle of JavaScript libraries and have full visibility and control.
This blog has been updated since its original publish date, June 11, 2019.