I have an angularjs template which looks similar to this:
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.)
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.