Angular DOM Sanitization in Ionic
Angular comes with built-in Cross Site Scripting (XSS) protection, which prevents you from using unverified dynamic values in certain contexts inside your generated page. Most of the time you shouldn't even notice this. But when you do, it's good to know how you can work around the restrictions set by this protection.
To demonstrate this, let's create a simple component which will allow you to render an SVG file using the following syntax:
<svg-image url="/assets/imgs/Ionic_Logo.svg"></svg-image>
Initial implementation will be really simple. The component will expose url
as the single input:
import { Component, Input } from '@angular/core';
@Component({
selector: 'svg-image',
templateUrl: 'svg-image.html'
})
export class SvgImageComponent {
@Input() url: string;
}
In the template, we'll render the SVG file using the <img>
element:
<div>
<img src="{{url}}">
</div>
To no surprise, this will work just fine. However, if we want to somewhat customize the rendered SVG using CSS, we might decide to render it using the <object>
element instead:
<div>
<object type="image/svg+xml" data="{{url}}"></object>
</div>
After applying the change, Angular's XSS protection will kick in and prevent the provided URL from being used as intended:
Error: unsafe value used in a resource URL context (see http://g.co/ng/security#xss)
Since the value used is constant and not provided as user input, we should be able to trust it. We need to somehow tell that to Angular so that it will use the value instead of throwing the error. For that purpose, Angular provides the DomSanitizer
class with several helper methods for bypassing the value validation in different contexts. According to the information provided by the error message, our value is treated as a resource URL:
import { Component, Input } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
@Component({
selector: 'svg-image',
templateUrl: 'svg-image.html'
})
export class SvgImageComponent {
safeUrl: SafeResourceUrl;
private urlValue: string;
@Input()
get url(): string {
return this.urlValue
};
set url(value: string) {
this.urlValue = value;
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(value);
}
constructor(private sanitizer: DomSanitizer) { }
}
In the updated code, we inject the DomSanitizer
into our component and mark the input url
as trusted whenever its value changes. In the template, we now need to use the trusted value instead of the original one:
<div>
<object type="image/svg+xml" data="{{safeUrl}}"></object>
</div>
If you try the above code, you'll notice that Angular will still throw the same error as before. This happens because we're binding the value using interpolation. In this binding mode, Angular will convert the value to string before injecting it into the template. By doing that, it will lose the information about the trust which we have provided.
To properly keep that information, we need to use property binding instead:
<div>
<object type="image/svg+xml" [data]="safeUrl"></object>
</div>
Now, the trust information will be preserved and Angular will finally render the SVG image as requested. A word of warning though: only use DomSanitizer
to bypass Angular's built-in security when you have full control over the values used. Otherwise you might expose yourself to a Cross Site Scripting attack.