πŸ›  Securing SPAs with Trusted Types

πŸ“˜ Learn how Trusted Types help eradicate DOM-based #XSS in frontend #Angular and #React applications.

TL;DR: XSS vulnerabilities remain a top threat to any HTML-based web application. In fact, 75% of web vulnerabilities in Google's reward program are XSS vulnerabilities! Fortunately, built-in platform defenses such as Content Security Policy and Trusted Types equip us with the tools to tackle XSS. In this article, we briefly look at modern XSS vulnerabilities, followed by a detailed look at using Trusted Types policies to mitigate such vulnerabilities. In the end, we provide you with a list of concrete developer guidelines to apply Trusted Types in your projects.

A Brief Intro to XSS

XSS, or Cross-Site Scripting, is a misnomer. Originally, XSS was a specific script injection attack between two websites. Today, the name XSS covers any vulnerability that allows the attacker to execute malicious code in the browsing context of the target application.

Modern XSS attacks can use JavaScript code but can just as well rely on CSS code or even HTML code. What matters is the result of the attack: malicious code running within the target application's execution context. When that happens, the malicious code can steal data or modify the behavior of the application.

But how can it get that far?

Before we dive deeper into XSS in modern JavaScript applications, let's take a look at a textbook XSS attack. Our example will focus on rendering a user-provided restaurant review.

How XSS turns data into code

The snippet below shows the server-side code to render a review. The code here is language-agnostic but corresponds to common patterns in languages such as PHP, Java, Node, etc.

foreach(review in reviews) {
    <div class="review">
      <h4>${review.title}</h4>
      <p>${review.text}</p>
    </div>
}

The snippet below shows two reviews from the database. Users of our restaurant review application have provided these reviews. As you can see, the first review is legitimate, but the second review contains malicious data.

// Review 1
Title: Friendly and delicious!
Text: The Restaurant is right in the center of town. It has top food en it's a very nice place with a friendly and professional staff

//Review 2
Title: Kitchen nightmares
Text: Fine location, lots of parking. But those are the only good things.<img src="none" onerror="console.log('Running malicious code :o')">

This last snippet shows the generated page that is sent to the browser. Note how the data variables in the template have been substituted with the actual review data.

<div class="review">
  <h4>Friendly and delicious!</h4>
  <p>The restaurant is right in the center of town...</p>
</div>
<div class="review">
  <h4>Kitchen nightmares</h4>
  <p>Fine location, ... things.<img src="none" onerror="console.log('Running malicious code :o')"></p>
</div>

These three code snippets illustrate the root cause of XSS vulnerabilities: confusion between data and code. In the first code snippet, the server is perfectly aware of which part of the template is code (the HTML) and which part is data (the variables). There is no confusion.

However, when the server sends this page to the browser, that context information is lost. The browser gets a very long string as input (the third snippet) but lacks the context information to distinguish data from code. As a result, what is HTML data for the server looks like HTML code for the browser, resulting in the execution of malicious JavaScript.

Mitigating XSS vulnerabilities

8