This article represents concepts and related instructions, code example/sample in relation to Cross-site scripting (XSS) security vulnerabilities in Angular app and how to prevent XSS attacks. This instruction in this article is valid for Angular 5.* and Angular 4.* versions.
Before we get started, read the related details on XSS on this page, Top 10 Angular security Best Practices.
This article will look into some of the following details in relation to XSS vulnerability and how attacks due to XSS can be prevented using Angular out-of-the-box utilities.
- What is Cross-site Scripting (XSS) Attack?
- Different types of XSS Vulnerabilities
- Angular approach to prevent XSS attacks
- Angular recommendations to prevent Server XSS attacks
- Angular recommendations to prevent Client XSS attacks
What is Cross-site Scripting (XSS) Attack?
Cross-site scripting (XSS), a security vulnerability in a web app, refers to an attacker sending a script to another user by means of injecting the malicious script into a trusted website. As the website is trusted, users end up opening the website like ever before which results in the browser executing the malicious script. The execution of malicious script can do some of the following:
- Change the DOM tree, thereby, rewriting the HTML page
- Access the cookie, session token information stored by the browser
Take a look at the following scenario:
- Lets say a website is not escaping the script related HTML tags in comment section.
- An attacker as a result of hacking exercise identifies this vulnerability.
- The attacker sends the malicious script as part of comment.
- The website goes on to send the script in response when comments are tried to be retrieved
- As a result of above, the malicious script gets executed.
Following represents the XSS attack diagrammatically:
XSS has been chosen as one of the top 10 security vulnerability by OWASP (open web application security project). Read the details on this page, 2017 Top 10 security vulnerability in a web app.
Different types of XSS Vulnerabilities
We will look into how Angular helps prevent the attack due to following different types of XSS vulnerabilities:
- Client XSS: Client XSS refers to the vulnerability when the untrusted data (such as malicious script) ends up modifying/changing the DOM tree resulting in theft of user data (session tokens, cookie etc) or altering the HTML page. The untrusted data can arrive in the user-generated request which is immediately sent back as HTTP response (reflected XSS) or, the untrusted data get originate from the data stored in the database (stored XSS). The untrusted data can also arrive solely from client-side script injection in browser. This implies that the source of the data is in the DOM, the sink is also in the DOM, and the data flow never leaves the browser. This type of XSS attack is also termed as DOM based XSS attack.
- Server XSS: Server XSS refers to the vulnerability when server sends the untrusted data (such as malicious script) as HTTP response to client side without proper validation. As like client XSS, the untrusted data can be generated as a result of reflected or stored XSS as mentioned in preceding point.
Angular approach to prevent XSS Attacks
Angular considers all data as untrusted data. Thus, by default, it sanitizes all data. This essentially means that any HTML tags found in your data is escaped. In order for you to still be able to show up HTML data on your page as it is, Angular recommends using different techniques such as using DOMSanitizer API etc., to sanitize the data such that data iss transformed in the form which is safe enough to be inserted into the DOM tree. This is discussed later in this article.
Angular recommendations to prevent Server XSS Attacks
The primary requirement for avoiding XSS attack is to prevent untrusted data (malicious script/code) from getting injected into the DOM tree. Attackers able to enter such malicious script into DOM tree can said to have successfully carried out XSS attack.
- As part of server-side processing, escape all data before sending them as Http response. That would mean that if response data consisted of HTML/Javascript tags, they will get escaped.
- Avoid generating Angular templates as part of server-side processing. This may lead to template injection thereby resulting in DOM manipulation when the page loads in the browser.
Angular recommendations to prevent Client XSS Attacks
The primary goal to prevent client XSS attacks to avoid modification of DOM tree as a result of execution of malicious script execution. Note that this malicious script can arrive as a result of reflected or stored XSS attack. Angular recommends some of the following technique to avoid client XSS attacks:
- Use innerHTML property for displaying content with HTML markups: Angular provides an innerHTML property which can be used to prevent automatic santization of all the data to be displayed as part of the components’ view. The HTML code snippet consisting of HTML tags can be sanitized exclusively from unsafe script tag element by assigning it to innerHTML property of an element in a template, as shown below. The unsafe tag elements such as script are stripped off from the content. Using innerHTML property would help in sanitizing the server response data from script injection while making sure the HTML elements are displayed as trusted data.
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { htmlSnippet = '<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Ealert(%22Hello%20World!%22)%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" /> <b>How are you doing?</b>'; }
The following is the template code, app.component.html, as referred in the above component. Notice the member variable of the component, htmlSnippet, comprising of HTML code snippet.
<div> <div>{{htmlSnippet}}</div> <div [innerHTML]="htmlSnippet"></div> </div>
The above template when loaded in the page gets displayed like following:
<script>alert("Hello World!")</script> <b>How are you doing?</b></pre> alert("Hello World!") <strong>How are you doing?</strong> <pre>
Make a note of some of the following:
- Interpolated content represented using {{htmlSnippet}} is escaped; This is inline with the fact that Angular escapes all the data, by default.
- Property htmlSnippet when assigned to [innerHTML] property leads to removal of script tag and display the rest of the HTML content. Content within b tag is considered safe and hence, not sanitized, as a result of which the content (How are you doing?) is turned into BOLD. Angular considers script tag as unsafe and automatically sanitizes it (by removing or stripping the script element). When you look into browser console log (CTRL+Shift+I), you would find a warning message such as WARNING: sanitizing HTML stripped some content.
- Use DOMSanitizer APIs for preventing automatic sanitization by Angular: As like innerHTML property discussed in preceding section, Angular provides DOMSanitizer bypassSecurityTrustXXX APIs which can be used for preventing automatic sanitizatio of data. The following is the API details:
- bypassSecurityTrustHtml (Prevents the sanitization of HTML content):
import { Component } from '@angular/core'; import {DomSanitizer} from '@angular/platform-browser'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { htmlSnippet = '<script>alert("Hello World!")</script><b>How are you doing?</b>'; trustedHtmlSnippet; constructor(private sanitizer: DomSanitizer) { this.trustedHtmlSnippet = this.sanitizer.bypassSecurityTrustHtml(this.htmlSnippet); } }
This is how the template looks like:
<div class="container"> <p>{{htmlSnippet}}</p> <p [innerHtml]="trustedHtmlSnippet"></p> </div>
The trustedHtmlSnippet gets displayed without script tag. In case, you put trustedHtmlSnippet as interpolated content, the following error message shows up on UI: SafeValue must use [property]=binding:… (see http://g.co/ng/security#xss).
- bypassSecurityTrustUrl (Prevents the sanitization of URL): Pay attention to DomSanitizer instance passed to constructor, and, bypassSecurityTrustUrl API being invoked on DomSanitizer instance. The API bypassSecurityTrustUrl returns object of type SafeHtml.
# Component app.component.ts # import { Component } from '@angular/core'; import {DomSanitizer} from '@angular/platform-browser'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { htmlSnippet = 'javascript:alert("Hello World!")'; trustedHtmlSnippet; constructor(private sanitizer: DomSanitizer) { this.trustedHtmlSnippet = this.sanitizer.bypassSecurityTrustUrl(this.htmlSnippet); } }
This is how the template looks like:
<div> <a [href]="htmlSnippet">Click Untrusted URL</a> <br> <a [href]="trustedHtmlSnippet">Click Trusted URL</a> </div>
Clicking on trusted URL link opens up an alert dialog box. However, clicking on untrusted URL does not as Angular, internally, prepends the URL with unsafe:. Thus, the URL becomes something like .
- bypassSecurityTrustResourceUrl (Prevents the sanitization of Resource URL)
- bypassSecurityTrustScript (Prevents the sanitization of Script)
- bypassSecurityTrustStyle (Prevents the sanitization of Style)
- bypassSecurityTrustHtml (Prevents the sanitization of HTML content):
- Direct Usage of DOM APIs must be avoided: Angular, rather, recommends using templates wherever possible. One should avoid direct usage of DOM API such as ElementRef. The browser built-in DOM APIs do not automatically sanitizes the data. Thus, the script can be injected, thereby, creating XSS vulnerability. Take a look at code example below for using DOM API such as ElementRef.
import {AfterViewInit, Component, ElementRef} from '@angular/core'; import {DomSanitizer} from '@angular/platform-browser'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private elementRef: ElementRef, private sanitizer: DomSanitizer) { const s = document.createElement('script'); s.type = 'text/javascript'; s.textContent = 'alert("Hello World")'; this.elementRef.nativeElement.appendChild(s); } }
As the component gets loaded, the alert box shows up. Had the HTML data consisting of Javascript alert method assigned to innerHTML property or passed through bypassSecurityTrustHTML API of DomSanitizer, it would have got sanitized.
- Content security policy (CSP): CSP should be set appropriately to avoid XSS attacks. All that is required to be done is to set content-security-policy as part of HTTP header. The following represents an example:
<meta http-equiv="Content-Security-Policy" content="default-src https://vitalflux.com; child-src 'none'; object-src 'none'">
- OpenAI GPT Models in 2024: What’s in it for Data Scientists - December 30, 2024
- Collaborative Writing Use Cases with ChatGPT Canvas - December 29, 2024
- When to Use ChatGPT O1 Model - December 28, 2024
I found it very helpful. However the differences are not too understandable for me