Wednesday, September 7, 2016

Angular 1.6 - Expression Sandbox Removal

Important Announcement

The Angular expression sandbox will be removed from Angular from 1.6 onwards, making the code faster, smaller and easier to maintain.

The removal highlights a best practice for security in Angular applications: Angular template, and expressions, should be treated similarly to code and user-provided input should not be used to generate templates, or expressions.
Removing the expression sandbox does not change the security surface of Angular 1 applications. In all versions of Angular 1, your application is at risk of malicious attack if you generate Angular templates using untrusted user-provided content (even if the content is sanitized to contain no HTML). This is the case with or without the sandbox and the existence of the sandbox only made some developers incorrectly believe that the expression sandbox protected them against such attacks.

What is the expression sandbox?

Angular puts an emphasis on the idea of separating business logic from user interface rendering. Application business logic should always reside in the code of controllers and components, where it can be unit tested effectively and more easily maintained.
Angular expressions were designed to allow a limited subset of JavaScript inside templates to support the basic logic that is necessary to be able to render the user interface. Angular templates encourage a clear separation of concerns between the simple logic used in rendering and the more complex logic of the application business domain.

The expression sandbox is a mechanism that checks Angular expressions to attempt to prevent accidental access to arbitrary JavaScript code and to discourage business logic from appearing in templates.

Why did we add the sandbox?

We added the sandbox to check that applications were not naively running JavaScript in their expressions since this implies that the template could be doing too much work. The aim was to provide feedback to the developer to prevent them from inadvertently designing applications that would be difficult to test and maintain.
Some time after the sandbox appeared in the codebase, a developer noticed that they could create an XSS attack to an Angular application if they had access to the template such that they could insert an arbitrary Angular expression. A number of these attacks were due to vulnerabilities in the sandbox combined with access to the template. Examples of these have been published on the web:
  • This blog post describes an attack that can be made if the template contains user provided content.
Initially we thought that we could tighten up the sandbox to prevent these attacks. But it became apparent that this was not an adequate defense: control of the Angular templates makes applications vulnerable even when the sandbox is completely secure:
  • This blog post shows a (now closed) vulnerability in the Plunker application due to server-side rendering inside an Angular template.
  • This blog post describes an attack, which does not rely upon an expression sandbox bypass, that can be made because the sample application is rendering a template on the server that contains user entered content.
The only effective security strategy is to ensure that users are never able to provide content that will be used in an Angular template, or expression.

As long as the Angular templates, and expressions, are not constructed using user provided content, there is no possibility of attack via these methods.

Why are we removing the sandbox?

While the sandbox was not a security defense mechanism, developers kept relying upon it as a security feature even though it was always possible to access arbitrary JavaScript code if a malicious user could control the content of Angular templates in applications.

Unfortunately we continued to patch up Angular 1 whenever people found new ways to undermine the sandbox despite the fact that it was not a defense mechanism.

These patches sent the wrong message to developers that they could continue to rely upon the sandbox for security, when they could not.

Removing the sandbox has the following benefits:

  • It clarifies to developers that Angular expression sandbox is not a security feature
  • It simplifies the parser codebase making it easier to maintain and extend.
  • It reduces the size of the parser codebase and therefore the size of the core Angular library distribution file (~1.3% smaller for angular.min.js.gzip).
  • It removes extra checks thus improving the speed and performance of Angular at runtime (~14% faster for complex expression parsing).
  • It provides opportunities to add further performance improvements to the parser.

You can view and comment on the Pull Request that removes the sandbox here.

What are the security implications?

If an attacker has access to control Angular templates or expressions, they can exploit an Angular application regardless of the version. There are a number of ways that templates or expressions can be controlled:

  • Generating Angular templates on the server containing user-provided content
  • Passing user-provided content in calls to these methods on a scope:
    • $watch(userContent, ...)
    • $watchGroup(userContent, ...)
    • $watchCollection(userContent, ...)
    • $eval(userContent)
    • $evalAsync(userContent)
    • $apply(userContent)
    • $applyAsync(userContent)
  • Passing user provided content in calls to services that parse expressions:
    • $compile(userContent)
    • $parse(userContent)
    • $interpolate(userContent)
  • Passing user provided content as the predicate parameter to the orderBy filter:
    • {{ value | orderBy : userContent }}

Each version of Angular 1 up to, but not including, 1.6 reduced the surface area of the vulnerability but never removed it.

If you dynamically generate Angular templates or expressions from user-provided content then you are at risk of XSS whatever version of Angular you are using.

If you do not generate your Angular templates or expressions from user-provided content then you are not at risk of this attack whatever version of Angular you are using.

See the Angular 1 security guide (once the PR has landed) for more information about the sandbox and possible vulnerabilities if you allow access to Angular templates.

What should I do?

If all your templates are static HTML files, or files that are generated without any user-provided input (e.g. using jade), and you are not generating any Angular expressions from user-provided content, then no action is needed and all the security measures provided by Angular are in effect.

If you dynamically generate Angular templates, or expression, using user-provided content then you should conduct a security review of your deployment and assess the risk that the user-provided content poses.

If you have to keep on using the user-provided content then the safest option is to ensure that it is only present in the part of the template that is made inert via the ngNonBindable directive. Be aware that you must ensure that all HTML is sanitized from the user content to prevent a malicious user from closing the tag containing the ngNonBindable directive and getting access to the rest of the template.

Size and performance comparison

Initial benchmarks of the removal show a small file size decrease and an increase in performance of the parser:

  • Gzipped size (angular.min.js.gzip) is 734 bytes smaller (1.3% smaller)
  • Complex expression parsing benchmark 76ms less time (14% faster)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.