Technical Insight

The Security Angle on Angular

Overview

We are actively working on TypeScript support within our WhiteHat Sentinel Source offering. One of the most common tasks performed while supporting a new language is the manual review of customer source code in an effort to identify gaps in the source rules. Armando Sosa (Sr. Software Engineer), Jamesley Norvil (Application Security Researcher), and I were tasked with carrying out such a manual review. This blog attempts to summarize our conclusions and findings.

Angular is a front-end component-based framework written in TypeScript. The risks associated with front-end technologies are drastically limited to their server-side counterparts, but still include cross-site scripting (XSS) and sensitive data disclosure. As is the case with most modern frameworks, security was one of the considerations early in the development of Angular. By default, the Angular framework will automatically implement contextual output encoding in alignment with OWASP’s XSS Prevention Cheat Sheet. Introducing XSS in an Angular application is certainly possible, but you have to explicitly disable built-in security mechanisms. Angular does not necessarily provide any controls to prevent sensitive data disclosure. As a result, understanding what sensitive data you collect and where you store it (ex: localStorage) is still an area of concern. For the purpose of this article, we will focus largely on XSS.

The Angular website does a great job of setting baseline security understanding. It also serves as the basis of a game-plan when performing a manual code review. In addition, we expanded this list to assess how key security relevant Angular components and HTML5 APIs were used and or mis-used. Such modules include but are not limited to: HttpClientXsrfModule, XSRFStrategy, HttpClientJsonpModule, CookieXSRFStrategy, sessionStorage and localStorage.

While we reviewed the Angular code for all of these known security risks and security relevant components, there were a couple observations that really stood out.

 

Old Patterns Persist

With all the craze over new front-end frameworks that abstract away the DOM, we still found use of jQuery or even raw DOM element access embedded within the application. We speculate that this was the result of developers in the process of learning the ins-and-outs of Angular and, as a result, periodically relied on old patterns. With respect to security, there are a number of benefits from using a front-end technology like Angular – strive to complete the task the “Angular way” to avoid the temptation to use old patterns.

// DOM

var name = window.location.hash;

var element = document.getElementById('element');

element.innerHTML = 'Hello, ' + name;

 

// jQuery

var expr = window.location.hash;

$('.element ' + expr).each(function(e) { ... });

 

Bypass Security APIs

We were particularly surprised at how common it was for Angular developers to make use of the bypassSecurityTrust family of APIs. These API are built to specifically disable the aforementioned contextual output encoding strategies that help prevent XSS. Use of these API was most common in the context of an Angular Pipe. Pipes are used within Angular attribute expressions to transform data before exposing it within the view. This is a particularly dangerous practice as this may introduce XSS, depending on the original source of the data. If the source of the data passed through these pipes comes from location.hash, for example, then we have a valid XSS vulnerability. Understanding where the data is coming from is becoming increasingly difficult with the enterprise-like growth of front-end framework abstractions. Users beware!

@Pipe({ name: ‘trustHtml’ })

export class TrustHtmlPipe implements PipeTransform {

     constructor(private sanitizer: DomSanitizer) { }

 

     transform(value) {

          return this.sanitizer.bypassSecurityTrustHtml(value);

     }

}

 

Dynamic Component Construction

I’ll admit, this one was truly unexpected. After 15+ years in this space, it is bittersweet to still be surprised in this way. TypeScript, the language used by Angular, is statically type. As such, I made some (incorrect) assumptions about what could and could not be done in the front-end. It turns out that Angular provides developers with the ability to dynamically compile and expose components at runtime given a template string. There’s even a detailed Stack Overflow article describing how this can be done. Angular contains an internal Compiler API that can be programmatically invoked under certain circumstances. Now imagine a scenario that the template string passed to this internal Compiler API were in part controlled by a malicious hacker. Game over – the hacker would have full control of the browser environment with the ability to perform any action as could be performed by the original Angular code. Please be mindful of this pattern! Really ask yourself if such a capability is necessary.

function compileComponent(template: string) {

     @Component({ template: template })

     class DynamicComponent implements AfterViewInit {

          // redacted implementation

     }

     return DynamicComponent;

}

 

Conclusion

Gone are the days when we would write code like $(element).html(…) (we still love you jquery!). Rich front-end frameworks like Angular have dramatically changed the landscape of development, improving security along the way. While it is certainly possible to introduce vulnerabilities through mis-use of Angular API, but you really have to go out of your way to do so. This trend will continue as new libraries and frameworks are built. Security is in the forethought of open source developers, and this is evident in the default behaviors and capabilities of their work.

Tags: application security, csrf, SAST, Secure Coding, typescript, XSS