Now for this blog post, we’ll be leaning on to AngularJS, it’s $sce service in particular. Often in coding projects, we get to encounter strings which contain HTML elements that should be rendered as html elements themselves. For example: markdowns, user generated html strings, etc.
And often, the frameworks we use don’t dispaly them as it should be (for security), rather, as simple strings. That is why we can use their sanitizing methods (such as html_safe for Ruby on Rails) by which we declare that we trust the string and we want it displayed as HTML (whatever the contents are).
Here’s a Rails example:
We have two strings with two versions each, one that is raw and another one that is declared safe with html_safe.
When we load the page containing these:
We see that our JS script was ran once (alert flashed),
And also a string containing our string was printed. Similar to the strong tag, in the html_safe version, <strong> was evaluated and led to bold letters.
Now, that we have already got the hang of it, let’s now focus on Angular JS and its $sce service.
Similar to Rails’ html_safe that we saw a while ago, AngularJS has a similar version, the ng-bind-html directive. And let’s see an example.
Here we have a simple AngularJS application with a textarea with myHtml assigned as the ng-model. And then two divs attached to the myHtml ng-model as well. These divs are listening to any value changes with the model and reflects them accordingly.
What sets these two divs apart is that one is bounded via the simple ng-bind while the other one is bound by ng-bind-html.
Let us see the difference:
For the div that binded to myHtml using the simple ng-bind, the value was just printed as a string while for the second div which was bound using ng-bind-html, the html was evaluated and the string in bold letters was shown.
Buuuut, why won’t we want something like this all the time? It is because we can’t always trust user input or interpolated for that matter. What if some bad guy injects malicious snippets of code into a seemingly clean html text like the following:
As we see above, the line seems clean and safe – just a string with a series of characters and one (cute) emoticon. But then when we (accidentally or intentionally) hover on it, KABOOM:
JK. It’s not really just a string, it actually has a onmouseover event attached to it. Our actual string was:
This has indeed some security concerns! So to solve, angular actually has the $sce service, short for Strict Contextual Escaping, which ensures and
requires that bindings in certain contexts to result in a value that is marked as safe to use for that context
If you’re using Angular version 1.2 or higher and you tried copying the above snippets of code, you may notice that it results to an error:
And this is because the $sce is already loaded and enabled by default in AngularJS versions 1.2 and higher. And as we mentioned earlier, $sce ensures and requires that binding result to a value that is marked as safe. (And we haven’t explicitly marked our input to be safe or trusted).
To demo the above data bindings without explicitly marking our html values as safe, I just disabled the $sce in my AngularJS version 1.4.1 to demonstrate how the ng-bind-html have behaved before $sce was loaded by default.
Note: Disabling the $sce module is highly discouraged as disabling it is like stripping away one security layer from your app.
Btw, if you’re curious and want to know how to disable $sce, you just need to set the enabled attribute of the $sceProvider to false in the config section of your module:
$sce, $sceProvider, $trustAs*
So now, let’s work on the default behaviour of AngularJS (1.2 onwards) – with $sce enabled. So either we just set enabled to true or just erase the config part completely as it defaults to true.
So now going back to the error above, let’s try to fix it. With $sce enabled we are saying that the value should be sanitized and marked as safe first. So what we can do is use Angular’s trustAs* methods.
Here we are saying that we explicitly trust the contents and it is safe to display it as it is with whatever the original value is.
The behaviour is similar to how our app a while ago behaved when $sce is disabled. For this one, our explicit consent was asked for first before the HTML was rendered.
What we can take from here is that explicitly trusted html/strings/values already bypasses any checks (but note that the CORS rules from your browser still apply and might impose stricter rules).
There are still many variations to the trustAs* method. There is trustAsJs, trustAsCss among others but they are used more or less the same way and what just differs is the type of value that they wrap.
In case you were wondering how to clean and sanitize data, AngularJS provides the ngSanitize module which, when loaded, automatically cleans / sanitizes your HTML string, already getting rid of hiding JS scripts, etc.
To use ngSanitize:
- Include angular-sanitize.js to your page, you may get it via CDN or save it locally. You may see the instructions here.
- Load ngSanitize into your Angular app:
- And that’s it! ngSanitize is now loaded and ready to clean your bound values.
Say we have the following:
Hovering on “Anybody here?” doesn’t yield any alerts. Due to ngSanitize’s automatic cleaning, even if we didn’t explicitly parse clean our string, the sneaky script was automatically weeded out. We also did not need to use trustAsHtml on our controllers as once our string have passed ngSanitize’s secure parsing methods, our string is already marked as trusted and safe for display.
So there, we saw a few security features that AngularJS provides! Hope you’ll find them useful anywhere you see fit to further improve your app’s security. 🙂
Thank you so much for reading!
Wishing you a great and lovely weekend ahead,