I am trying to get third-party (potentially unsafe) html content from my database and insert it into my html document.
How do I safely do that (Protection against XSS) ?
In Angular1.x there used to be $sce
to sanitize input, how do I do that in Angular2 ? As far as I understand, Angular2 automatically sanitizes it by default, is that correct ?
Something like this will not work:
<div class="foo"> {{someBoundValueWithSafeHTML}} // I want HTML from db here </div>
Sanitization and security contextslinkSanitization is the inspection of an untrusted value, turning it into a value that's safe to insert into the DOM. In many cases, sanitization doesn't change a value at all. Sanitization depends on context: A value that's harmless in CSS is potentially dangerous in a URL.
Angular treats all values as untrusted by default. When a value is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation, Angular sanitizes and escapes untrusted values. Sanitization modifies the input, turning it into a value that is safe to insert into the DOM.
Angular comes with a built-in html sanitizer DomSanitizer , as a security feature, thats used whenever you use [innerHtml] .
Use the sanitize() Method to Sanitize String in JavaScript It should be used when the data is already available as DOM nodes, such as when sanitizing a Document instance in a frame. The default sanitizer() configuration automatically strips out XSS-relevant input, including script tags, custom elements, and comments.
To insert normal HTML into your angular2 app, you can use the [innerHtml]
directive.
<div [innerHtml]="htmlProperty"></div>
This is not going to work with HTML which has its own components and directives, at least not in the way you'd expect it.
If however you do get an unsafe html warning you should trust the HTML
first before injecting it. You have to use the DomSanitizer
for such a thing. For instance, an <h3>
element is considered safe. An <input>
element is not.
export class AppComponent { private _htmlProperty: string = '<input type="text" name="name">'; public get htmlProperty() : SafeHtml { return this.sr.bypassSecurityTrustHtml(this._htmlProperty); } constructor(private sr: DomSanitizer){} }
And have your template stay the same as this:
<div [innerHtml]="htmlProperty"></div>
A little heads-up though:
WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
If you plan on using this technique more, you can try to write a @Pipe
to fulfill this task.
import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Pipe({ name: 'trustHtml' }) export class TrustHtmlPipe implements PipeTransform { constructor(readonly sr: DomSanitizer){} transform(html: string) : SafeHtml { return this.sr.bypassSecurityTrustHtml(html); } }
If you have a pipe like this, your AppComponent
will change to this. Don't forget to add the pipe to your declarations array of your NgModule
:
@Component({ selector: 'app', template: `<div [innerHtml]="htmlProperty | trustHtml"></div>` }) export class AppComponent{ public htmlProperty: string = '<input type="text" name="name">'; }
Or you can write a @Directive
to do the same:
@Directive({ selector: '[trustHtml]' }) export class SanitizeHtmlDirective { @Input() public set trustHtml(trustHtml: string) { if (this._trustHtml !== trustHtml) { this._trustHtml = trustHtml; this.innerHtml = this.sr.bypassSecurityTrustHtml(this.trustHtml); } } @HostBinding('innerHtml') innerHtml?: SafeHtml; private _trustHtml: string; constructor(readonly sr: DomSanitizer){} }
If you have a directive like this, your AppComponent
will change to this. Don't forget to add the directive to your declarations array of your NgModule
:
@Component({ selector: 'app', template: `<div [trustHtml]="htmlProperty"></div>` }) export class AppComponent{ public htmlProperty: string = '<input type="text" name="name">'; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With