How is concatenating urls in templates in angular less secure than in other locations?

I have an angularjs template which looks similar to this:

<img ng:src="/resources/{{id}}/thumbnail" />

However this results in an $interpolate:noconcat error. In contrast to that this template:

<img ng:src="{{fullUrl}}" />

or even:

<img ng:src="{{id|createThumbnailURL}}" />

(where createThumbnailURL is a simple filter which does the same concatination as above) work totally fine.

The documentation says:

Concatenating expressions makes it hard to reason about whether some combination of concatenated values are unsafe to use and could easily lead to XSS.

Well yes, a static URL is always easier to assess than a concatenated one, I see the point there. However it does not sound uncommon to me to have REST-APIs that have URLs that can be constructed by simple concatenation and that concatenation has to be done somehwere. I can do it in the controller or even server-side, but how does that improve anything to move the concatenation elsewhere? And what is the recommended way to deal with the problem?

UPDATE

Here is demo for the error: http://cipher-code.de/tmp/angular3/index.xhtml

Maybe it has to do with the page being XML.

Answers:

Answer

This is called SCE (Strict Contextual Escaping): Like many "strictness" modes, this is configurable. But as of V 1.2 it is automatically set to true.

More specifically, in contexts Angular considers to be vulnerable (like url's), there is less interpolation allowed (Strictness). Your URL concatenation is being "sanitized".

You are already aware of the reason: XSS attacks. It's also used for the developer's protection: a slightly wrong url could cause data deletes or overwriting.

You're probably confused why full string interpolation ng:src="{{fullUrl}}" is so much safer than string concatenation ng:src="/resources/{{id}}/thumbnail". TBH, I'm not sure there's a serious difference, but these are judgement calls.


You have some alternatives for dealing with this annoyance:

1) Wrap your url construction inside $sce.trustAs()

<img ng:src="sce.trustAs('url', '/resources/{{id}}/thumbnail')" />

2) You can disable SCE across your application, if you choose

angular.module('myApp').config(function($sceProvider) {
    $sceProvider.enabled(false);
});

Correction:

You can't call the $sce service from a directive. Only the $scope service is directly available. But you can use a function (or a directive that uses a function).

    $scope.createUrl = function (strName) {
        var truststring = '/resources/' + strName + '/thumbnail';

        return truststring;
    }

and your directive call would look like

<img ng:src="{{ createUrl(id) }}" />

In this case, if you wrap your concatenation in a function, you may not even need to de-sanitize it since you won't be breaking SCE rule.

Answer

I joined the party a little late, but here are my two cents:

In (very) plain words:

(1)
Angular uses the $sce service to perform "Strict Conceptual Escaping" (SCE). Basically, SCE "requires bindings in certain contexts to result in a value that is marked as safe to use for that context".

It is a protection mechanism provided by Angular to developers to easily protect (some aspects of) your app (this is even more important for apps that bind values based on user's input).

(2)
Some places (i.e. attributes) are considered more vulnerable to certain kinds of attacks (e.g. XSS) and SCE uses more strict rules for them. A few examples are a form's action or a frame's or object's src/ngSrc.

(3)
Based on Angular's current implementation (v1.2.16), an error will be thrown when such a vulnerable attribute is assigned a value that consists of more than 1 "part". A part (accoding to $interpolate service's parsing algorithm is either a part of the expression enclosed in {{ }} or a part of the expression that is outside of {{ }}.

In your case, /resources/{{id}}/thumbnail is parsed into 3 parts:
/resources/, {{id}}, /thumbnail


So, what is the problem with more than 1 part ???

There is nothing inherently more insecure in concatenating in the view vs concatenating in the controller and using a single-part expression.

Quoting from Angular's source (emphasis mine):

// Concatenating expressions makes it hard to reason about whether some combination of
// concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
// single expression be used for iframe[src], object[src], etc., we ensure that the value
// that's used is assigned or constructed by some JS code somewhere that is more testable or
// make it obvious that you bound the value to some user controlled value. This helps reduce
// the load when auditing for XSS issues.

So, it is Angular's way to force a "more secure" practice on you. Constructing the value in the view is less testable and less explicit, so you (or someone else) will have a harder time auditing the app for XSS vulnerabilities (increasing the probability of security issues).


There are a couple of ways (already mentioned in DaveA's answer) to circumvent this. (I would definitely not entirely disable $sce, though.)

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.