Software Integrity

 

AngularJS: Preventing common vulnerabilities in the MEAN stack

preventing mean stack angularjs vulnerabilities

Before jumping into the latest post within our discussion on vulnerabilities in the MEAN stack, look back at the first three posts discussing MongoDBExpressJS (Core), and ExpressJS (Sessions and CSRF).

AngularJS disabled SCE service

Angular 1.2 and greater include the built-in Strict Contextual Escaping service ($sce) by default. This service strips malicious HTML tags (e.g., <script>, etc.), attributes (e.g., onmouseover, onerror, etc.), and URI protocols (e.g., javascript) from data rendered as HTML with the ng-bind-html directive. This service can be disabled globally with the $sceProvider.enabled() method in the controller’s config block or per-instance with the $sce.trustAs methods. Thus, leaving the application vulnerable to cross-site scripting (XSS) attacks when binding untrusted data as HTML.

Consider the excerpt below from our example application where the $sce service is disabled and the untrusted user input is rendered with the ng-bind-html directive.

preventing mean stack angularjs vulnerabilities

If we submit the payload <img src=x onerror=alert(‘XSS_SUCCESS!’)></img> we can generate an XSS exploit.

XSS exploit with SCE disabled

XSS exploit with SCE disabled

The simplest solution is to leave the $sce service enabled for all untrusted input bound to the ng-bind-html directive. Removing the $sceProvider.enabled(false) method from the excerpt above means the malicious onerror attribute will be sanitized appropriately.

SCE strips malicious onerror attribute and prevents XSS exploit

SCE strips malicious onerror attribute and prevents XSS exploit

Alternatively, the data could also be rendered with the ng-bind directive which renders the text outside of the HTML context and performs HTML encoding on all malicious characters, such as angle brackets (<>) or double quotes (“).

AngularJS expression injection

Angular templates (defined by the ng-app directive) use double curly braces (i.e., {{ and }}) to denote expressions that are evaluated by the Angular rendering engine. If an attacker can inject curly braces into the template, they can easily enter the code execution context. To further complicate matters, off-the-shelf HTML encoding or sanitizing functions do not handle curly braces.

Consider the login page for the example MEAN Bug application which reflects the username during a failed login attempt.

Username reflected in message

Username reflected in message

If we examine the source code, we see that the message is rendered as a text attribute, encoding all HTML control characters.

AngularJS: Preventing common vulnerabilities in the MEAN stack

If we attempt the following payload, we see that the <script> tags are encoded and the exploit does not work.

http://localhost:9000/login?user=<script>alert(‘XSS_SUCCESS!’)</script>

HTML characters encoded to prevent XSS

HTML characters encoded to prevent XSS

However, because the text is rendered within an HTML element bound by the ng-app directive, anything encapsulated by double curly braces will be evaluated as an Angular expression. If we submit the following request, we can enter a code execution context within an Angular expression.

http://localhost:9000/login?user={{constructor.constructor(‘alert(“XSS_SUCCESS!”)’)()}}

This request also contains a sandbox-escaping payload (i.e., constructor.constructor()) which allows us to break out of the Angular scope and do things like generate an alert message.

Angular expression injection used to execute XSS

Angular expression injection used to execute XSS

Break out of the Angular sandbox.

Remediate this vulnerability by sanitizing curly braces from untrusted input. Or, by preventing the input from being written inside an Angular template by reducing the scope of the ng-app directive (e.g., bind ng-app to a div or table element rather than the body element).

Alternatively, the ng-non-bindable directive can be used to tell the rendering engine to ignore any expressions within that element. The example application has been modified with this solution to prevent the expression-injection exploit from executing.

AngularJS: Preventing common vulnerabilities in the MEAN stack

Directive ng-non-bindable used to prevent expression injection

Directive ng-non-bindable used to prevent expression injection

AngularJS local storage information leakage

Angular’s angular-storage service allows applications to easily persist data client-side in web storage without the size restrictions imposed by cookies. However, this service defaults to using local storage which persists indefinitely, unless it is explicitly cleared. This data will remain on the user’s file system even after the browser is closed, exposing the data to any user with physical access to the system. The excerpt below demonstrates how the angular-storage service is used in its default configuration.

AngularJS: Preventing common vulnerabilities in the MEAN stack

The data persisted by the angular-storage method store.set is in the browser’s local storage.

Sensitive data stored in local storage will persist indefinitely

Sensitive data stored in local storage will persist indefinitely

Because any client-side data is inherently insecure, local storage should not be used to persist sensitive data such as credentials or credit card numbers. A slightly more secure alternative is to configure the angular-storage service to use session storage which is cleared automatically when the browser tab is closed. This can be done with the storeProvider service within the config block as shown below.

AngularJS: Preventing common vulnerabilities in the MEAN stack

Application configured to use session storage

Application configured to use session storage

This example uses the angular-storage module, but keep in mind that data may also be persisted to local storage (and session storage) via other Angular modules such as ngStorage and angular-locker or by calling $window.localStorage or $window.sessionStorage directly.

Stay tuned for Part 5 of this series, exploring NodeJS, which will drop on the Software Security blog on May 17th.