131

I want to set the background image of a DIV in a Component Template in my Angular 2 app. However I keep getting the following warning in my console and I don't get the desired effect... I am unsure if the dynamic CSS background image is being blocked due to security restrictions in Angular2 or if my HTML template is broken.

This is the warning I see in my console (I have changed my img url to /img/path/is/correct.png:

WARNING: sanitizing unsafe style value url(SafeValue must use [property]=binding: /img/path/is/correct.png (see http://g.co/ng/security#xss)) (see http://g.co/ng/security#xss).

The thing is I do sanitize what is injected into my template using the DomSanitizationService in Angular2. Here is my HTML that I have in my template:

<div>
    <div>
        <div class="header"
             *ngIf="image"
             [style.background-image]="'url(' + image + ')'">
        </div>

        <div class="zone">
            <div>
                <div>
                    <h1 [innerHTML]="header"></h1>
                </div>
                <div class="zone__content">
                    <p
                       *ngFor="let contentSegment of content"
                       [innerHTML]="contentSegment"></p>
                </div>
            </div>
        </div>
    </div>
</div>

Here is the component...

Import {
    DomSanitizationService,
    SafeHtml,
    SafeUrl,
    SafeStyle
} from '@angular/platform-browser';

@Component({
               selector: 'example',
               templateUrl: 'src/content/example.component.html'
           })
export class CardComponent implements OnChanges {

    public header:SafeHtml;
    public content:SafeHtml[];
    public image:SafeStyle;
    public isActive:boolean;
    public isExtended:boolean;

    constructor(private sanitization:DomSanitizationService) {
    }

    ngOnChanges():void {
        map(this.element, this);

        function map(element:Card, instance:CardComponent):void {
            if (element) {
                instance.header = instance.sanitization.bypassSecurityTrustHtml(element.header);

                instance.content = _.map(instance.element.content, (input:string):SafeHtml => {
                    return instance.sanitization.bypassSecurityTrustHtml(input);
                });

                if (element.image) {
                    /* Here is the problem... I have also used bypassSecurityTrustUrl */ 
                    instance.image = instance.sanitization.bypassSecurityTrustStyle(element.image);
                } else {
                    instance.image = null;
                }

            }
        }
    }
}

Please note that when I just bound to the template using [src]="image", for example:

<div *ngIf="image">
    <img [src]="image">
</div>

and image was passed using bypassSecurityTrustUrl everything seemed to work well... can anyone see what I am doing wrong?

isherwood
  • 58,414
  • 16
  • 114
  • 157
Mark Sandman
  • 3,293
  • 12
  • 40
  • 60
  • Did you get solution to your question. I have exactly the same issue and still trying to find a solution. Thanks in advance! – SK. Oct 08 '19 at 13:45

10 Answers10

122

You have to wrap the entire url statement in the bypassSecurityTrustStyle:

<div class="header" *ngIf="image" [style.background-image]="image"></div>

And have

this.image = this.sanitization.bypassSecurityTrustStyle(`url(${element.image})`);

Otherwise it is not seen as a valid style property

Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • 1
    PierreDuc, any words of wisdom for when background-image IS bypassed as above, but then Angular2 silently ignores it? I can post a new question but I think its fairly germane to your answer. – David Pfeffer Sep 06 '16 at 20:27
  • @DavidPfeffer It's difficult to judge where things go wrong without seeing any code :) I use this code in the latest angular2 and it's still working.. – Poul Kruijt Sep 07 '16 at 06:54
  • 1
    I figured it out. After you bypass sanitization, if the value is invalid, Angular2 silently ignores it. – David Pfeffer Sep 08 '16 at 12:14
  • You should ngStyle and it will just work without messing with sanitizing. – yglodt Aug 20 '19 at 10:52
  • 1
    Worked for me in Angular8. I think that sanitizing is best... it exists for a reason. @yglodt. – Sean Halls Jan 15 '20 at 16:22
78

Use this <div [ngStyle]="{'background-image':'url('+imageUrl+')'}"></div> this solved the problem for me.

Iredia Ebikade
  • 1,785
  • 14
  • 18
60

If background image with linear-gradient (*ngFor)

View:

<div [style.background-image]="getBackground(product.img)">
</div>

Class:

import { DomSanitizer, SafeResourceUrl, SafeUrl } from '@angular/platform-browser';

constructor(private _sanitizer: DomSanitizer) {}

getBackground(image) {
    return this._sanitizer.bypassSecurityTrustStyle(`linear-gradient(rgba(29, 29, 29, 0), rgba(16, 16, 23, 0.5)), url(${image})`);
}
Swapnil Patwa
  • 4,069
  • 3
  • 25
  • 37
  • 2
    It's not recommended to call `getBackground` inside the view, because Angular has to call `bypassSecurityTrustStyle` each time that view is refreshed. To test that add console.log inside `getBackground` and you will see that function is called on each click or user scroll event – Marcin Aug 15 '19 at 16:41
  • 1
    This worked for me! Thank u. I had to do a little modification for the code to work tho. I added this [link](https://ionicframework.com/docs/core-concepts/webview#file-protocol)`Capacitor.convertFileSrc(image)` to the code. To trust the image path. If you are using Cordova, then use this [link](https://ionicframework.com/docs/core-concepts/webview#file-protocol)window.Ionic.WebView.convertFileSrc(image) `getBackground(image) { return this._sanitizer.bypassSecurityTrustStyle(linear-gradient(rgba(29, 29, 29, 0), rgba(16, 16, 23, 0.5)), url(${Capacitor.convertFileSrc(image)})); }` – Eric Aig Feb 18 '21 at 07:45
22

I got the same issue while adding dynamic url in Image tag in Angular 7. I searched a lot and found this solution.

First, write below code in the component file.

constructor(private sanitizer: DomSanitizer) {}
public getSantizeUrl(url : string) {
    return this.sanitizer.bypassSecurityTrustUrl(url);
}

Now in your html image tag, you can write like this.

<img class="image-holder" [src]=getSantizeUrl(item.imageUrl) />

You can write as per your requirement instead of item.imageUrl

I got a reference from this site.dynamic urls. Hope this solution will help you :)

Arjun
  • 1,294
  • 13
  • 24
  • it works for images, but question was about URL in style, using as background image, which this answer is unrelated – Amirreza Dec 16 '19 at 09:17
11

Check this handy pipe for Angular2: Usage:

  1. in the SafePipe code, substitute DomSanitizationService with DomSanitizer

  2. provide the SafePipe if your NgModule

  3. <div [style.background-image]="'url(' + your_property + ')' | safe: 'style'"></div>

SimoneMSR
  • 368
  • 1
  • 6
  • 16
9

Based on the docs at https://angular.io/api/platform-browser/DomSanitizer, the right way to do this seems to be to use sanitize. At least in Angular 7 (don't know if this changed from before). This worked for me:

import { Component, OnInit, Input, SecurityContext } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

constructor(
    private sanitizer: DomSanitizer
) { }

this.sanitizer.sanitize(SecurityContext.STYLE, 'url(' + this.image + ')');

Re SecurityContext, see https://angular.io/api/core/SecurityContext. Basically it's just this enum:

enum SecurityContext {
  NONE: 0
  HTML: 1
  STYLE: 2
  SCRIPT: 3
  URL: 4
  RESOURCE_URL: 5
}
Dovev Hefetz
  • 1,346
  • 14
  • 21
  • 1
    This is the most up to date answer. It can be shortned too: `this.sanitizer.bypassSecurityTrustStyle(\`url('${this.image} ')\`);` – Zahema Nov 17 '19 at 06:20
  • 1
    @Zahema I don't believe that is equivalent to the answer provided. `bypassSecurityTrustStyle` ignores security while `sanitize(SecurityContext.STYLE, style)` reinforces security. I would recommend using `sanitize` with the appropriate `SecurityContext`. – Oscar Feb 11 '20 at 17:14
  • @Zahema `bypassSecurityTrustStyle` returns an Object that can not be accessed (at least I could not do it) in `[ngStyle]`. `sanitize(SecurityContext.STYLE, style)` instead returns a plain string. – afcode Feb 15 '20 at 08:50
  • @Oscar I agree but for some reason it doesn't always work as expected in all scenarios. `bypassSecurityTrustStyle` is basicly brute forcing it. – Zahema Feb 17 '20 at 06:51
3

There is an open issue to only print this warning if there was actually something sanitized: https://github.com/angular/angular/pull/10272

I didn't read in detail when this warning is printed when nothing was sanitized.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 3
    For those that might come here: that issue has been resolved. It only prints the warning IF it sanitized HTML and not all the time. – flamusdiu Apr 30 '17 at 09:16
  • I wanted to know is it wrong practice to do so ? Should i try not to get this warning? – Amrit Dec 03 '19 at 07:15
  • You should be **very** cautious when you apply this to user-provided content (like text from an input field or user content loaded from a database or other sources you don't control. This way you tell Angular that inherently unsafe content should be treated as trustworty. It's totally fine though to use it for static content that you control, like constants, environment variables passed at build time, values calculated only from such safe values. – Günter Zöchbauer Dec 03 '19 at 09:45
3

In my case, I got the image URL before getting to the display component and want to use it as the background image so to use that URL I have to tell Angular that it's safe and can be used.

In .ts file

userImage: SafeStyle;
ngOnInit(){
    this.userImage = this.sanitizer.bypassSecurityTrustStyle('url(' + sessionStorage.getItem("IMAGE") + ')');
}

In .html file

<div mat-card-avatar class="nav-header-image" [style.background-image]="userImage"></div>
3

In my case, I needed the functionality of sanitizing in more than one component. Implementing a pipe was the most suitable and elegant way for solving this problem.

Implementation:

@Pipe({
  name: 'sanitizer'
})
export class SanitizerPipe implements PipeTransform {

  constructor(private sanitizer: DomSanitizer) {}

  transform(value: any, args?: any): any {
    return this.sanitizer.bypassSecurityTrustUrl(value);
  }
}

Using the pipe:

<img [src]="pictureSrc | sanitizer" alt="selected picture">
teranshil
  • 109
  • 1
  • 6
1

For anyone who is already doing what the warning suggests you do, before the upgrade to Angular 5, I had to map my SafeStyle types to string before using them in the templates. After Angular 5, this is no longer the case. I had to change my models to have an image: SafeStyle instead of image: string. I was already using the [style.background-image] property binding and bypassing security on the whole url.

Hope this helps someone.

Jake Smith
  • 2,332
  • 1
  • 30
  • 68