14

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.

Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
yankee
  • 38,872
  • 15
  • 103
  • 162
  • pretty sure your first example should work. Here is a working sample: http://plnkr.co/edit/rCjtaoiWEczflENZl7AR Can you create a plunker that illustrates your issue? – Beyers May 17 '14 at 19:05
  • @Beyers: I added a link to a demo to my question. When you asked I first tried plunkr and I could not reproduce the error there. That came in unexpectedly... – yankee May 17 '14 at 20:25

2 Answers2

11

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.

Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
  • You could also use `getTrustedResourceUrl('/resources/{{id}}/thumbnail');` – Preview May 18 '14 at 09:08
  • I get the same error with both [trustAs](http://cipher-code.de/tmp/angular3/trustAs.xhtml) and even with [SCE disabled](http://cipher-code.de/tmp/angular3/disableSCE.xhtml). – yankee May 18 '14 at 10:56
  • @Aperçu: I get the same error with [getTrustedResourceUrl](http://cipher-code.de/tmp/angular3/getTrustedResourceUrl.xhtml) – yankee May 18 '14 at 10:56
  • @DaveA: Yes, this is would I am doing right now. Actually I am using a filter, but it does not make too much of a difference. I am still buffled, especially since there seem to be other rules in effect when the page is an XML page... And I still wonder what the people who made this rule would say about this. Too me it is just a hack to bypass SCE due to not knowing how it *should* be done. Would they say that this is the recommanded way, because it is easier to assess for XSS? – yankee May 18 '14 at 20:10
  • @yankee, all your observations are correct. SCE is implemented oddly. To the point where I'd even recommend to switch it off. You're by no means alone in seeing it as a nuisance rather than valuable feature. To be fair, Google is still new to providing languages for other developers to utilize. The way they use Angular is different than the way most Angular users do. This is a first step in "enforcing" good practices, and I'm sure they will adapt and evolve. Hopefully, they'll realize that Client development is very institutional and they'll try not to enforce any kind of standards. – Dave Alperovich May 18 '14 at 20:16
3

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.)

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • A lot of good points. Personally I'm torn about SCE. As a .NET developer I appreciate strictness as a tool. In the case of SCE, I feel it's too inconsistent to be useful. But I agree with you that SCE has value. And I believe Angular will continually grow. – Dave Alperovich May 24 '14 at 01:47