Angular Security: The Definitive Guide (part 2)

We are happy to share our methodology and security guide on how to do security reviews of Angular applications. In this article we delve into the risks posed by the misuse of Security Context and Security Trust methods, explore HTML injection vulnerabilities, and analyze the impact of user input on templates.

Angular is a versatile front-end framework that enables direct use of web APIs such as the Window, Document, and Node interfaces. It also has its own wrappers to manipulate the Document Object Model (DOM). Additionally, JavaScript libraries such as jQuery can be used with Angular.

In Part 1, we examined Angular's security model and framework architecture. In Part 2, we cover the following:

  • How misuse of Security Context and Security Trust methods leads to XSS, CSS injection, and Open Redirect
  • How HTML injection arises in the Angular application
  • How user input affects client-side and server-side templates

Bypass Security Trust Methods

Angular introduces a list of methods to bypass its default sanitization process and to indicate that a value can be used safely in a specific context, as in the following five examples:

  1. bypassSecurityTrustUrl is used to indicate the given value is a safe style URL:

    //app.component.ts
    this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');
    //app.component.html
    <a class="e2e-trusted-url" [href]="trustedUrl">Click me</a>
    //result
    <a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Click me</a>

  2. bypassSecurityTrustResourceUrl is used to indicate the given value is a safe resource URL:

    //app.component.ts
    this.trustedResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png");
    //app.component.html
    <iframe [src]="trustedResourceUrl"></iframe>
    //result
    <img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">

  3. bypassSecurityTrustHtml is used to indicate the given value is safe HTML. Note that inserting script elements into the DOM tree in this way will not cause them to execute the enclosed JavaScript code, because of how these elements are added to the DOM tree:

    //app.component.ts
    this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml("<h1>html tag</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
    //app.component.html
    <p style="border:solid" [innerHtml]="trustedHtml"></p>
    //result
    <h1>html tag</h1>
    <svg onclick="alert('bypassSecurityTrustHtml')" style="display:block">blah</svg>

  4. bypassSecurityTrustScript is used to indicate the given value is safe JavaScript. However, we found its behavior to be unpredictable, because we couldn't execute JS code in templates using this method:

    //app.component.ts
    this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");
    //app.component.html
    <script [innerHtml]="trustedScript"></script>
    //result
    -

  5. BypassSecurityTrustStyle is used to indicate the given value is safe CSS. The following example illustrates CSS injection:

    //app.component.ts
    this.trustedStyle = this.sanitizer.bypassSecurityTrustStyle('background-image: url(https://example.com/exfil/a)');
    //app.component.html
    <input type="password" name="pwd" value="01234" [style]="trustedStyle">
    //result
    Request URL: GET example.com/exfil/a

Angular provides a sanitize method to sanitize data before displaying it in views. This method employs the security context provided and cleanses the input accordingly. It is, however, crucial to use the correct security context for the specific data and context. For instance, applying a sanitizer with SecurityContext.URL on HTML content does not provide protection against dangerous HTML values. In such scenarios, misuse of security context could lead to XSS vulnerabilities.

HTML Injection

This vulnerability occurs when user input is bound to any of these three properties: innerHTML, outerHTML, or iframe srcdoc. While binding to these attributes interprets HTML as it is, the input is sanitized using SecurityContext.HTML. Thus, HTML injection is possible, but cross-site scripting (XSS) is not.

Example of using innerHTML:

//app.component.ts
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent{
//define a variable with user input
test = "<script>alert(1)</script><h1>test</h1>";
}
//app.component.html
<div [innerHTML]="test"></div>

The result is <div><h1>test</h1></div>.

Template Injection

Client-Side Rendering (CSR)

Angular leverages templates to construct pages dynamically. This approach entails enclosing template expressions for Angular to evaluate within double curly brackets ({{}}). In this way, the framework offers additional functionality. For instance, a template such as {{1+1}} would display as 2.

Typically, Angular escapes user input that can be confused with template expressions (e.g., characters such as <>`'"). It means that additional steps are required to circumvent this restriction, such as utilizing functions that generate JavaScript string objects to avoid using blacklisted characters. However, to achieve this, we must consider the Angular context, its properties, and variables. Therefore, a template injection attack may appear as follows:

//app.component.ts
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
@Component({
selector: 'app-root',
template: '<h1>title</h1>' + _userInput
})

As shown above: constructor refers to the scope of the Object constructor property, enabling us to invoke the String constructor and execute an arbitrary code.

See more payloads here.

Server-Side Rendering (SSR)

Unlike CSR, which occurs in the browser's DOM, Angular Universal is responsible for the SSR of template files. These files are then delivered to the user. Despite this distinction, Angular Universal applies the same sanitization mechanisms used in CSR to enhance SSR security. A template injection vulnerability in SSR can be spotted in the same way as in CSR, because the used template language is the same.

Of course, there also is a possibility of introducing new template injection vulnerabilities when employing third-party template engines such as Pug and Handlebars.

Read more about Angular Universal here.

Conclusion

Now you know how to sleuth out several vulnerabilities in Angular, such as bypassing Security Trust methods, HTML injection, and template injection. Stay tuned for Part 3, where we cover the most common DOM vulnerabilities in Angular. If you should need a recap of Angular's security model, don't hesitate to review the first part of our article!

You might also like

Angular Security: The Definitive Guide (part 1)
29/5/2023
Angular Security: The Definitive Guide (part 1)
We are happy to share our methodology and security guide on how to do security reviews of Angular applications. In this article we will talk about the architecture and security model of Angular to build a solid foundation for future parts.