DOM sanitization services for preventing Cross Site Scripting (XSS) attacks, with safe value types and bypass methods for trusted content. Essential for handling user-generated content and dynamic HTML.
Abstract service that provides sanitization for different security contexts and bypass methods for trusted content.
/**
* DomSanitizer helps preventing Cross Site Scripting Security bugs (XSS) by sanitizing
* values to be safe to use in the different DOM contexts.
*/
abstract class DomSanitizer implements Sanitizer {
/**
* Gets a safe value from either a known safe value or a value with unknown safety.
* If the given value is already a SafeValue, this method returns the unwrapped value.
* If the security context is HTML and the given value is a plain string, this method
* sanitizes the string, removing any potentially unsafe content.
* @param context - The security context in which the value will be used
* @param value - The value to sanitize
* @returns Sanitized string value or null
*/
abstract sanitize(context: SecurityContext, value: SafeValue | string | null): string | null;
/**
* Bypass security and trust the given value to be safe HTML.
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
* @param value - The HTML string to trust
* @returns SafeHtml value that bypasses sanitization
*/
abstract bypassSecurityTrustHtml(value: string): SafeHtml;
/**
* Bypass security and trust the given value to be safe style value (CSS).
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
* @param value - The CSS string to trust
* @returns SafeStyle value that bypasses sanitization
*/
abstract bypassSecurityTrustStyle(value: string): SafeStyle;
/**
* Bypass security and trust the given value to be safe JavaScript.
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
* @param value - The JavaScript string to trust
* @returns SafeScript value that bypasses sanitization
*/
abstract bypassSecurityTrustScript(value: string): SafeScript;
/**
* Bypass security and trust the given value to be a safe style URL.
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
* @param value - The URL string to trust
* @returns SafeUrl value that bypasses sanitization
*/
abstract bypassSecurityTrustUrl(value: string): SafeUrl;
/**
* Bypass security and trust the given value to be a safe resource URL.
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
* @param value - The resource URL string to trust
* @returns SafeResourceUrl value that bypasses sanitization
*/
abstract bypassSecurityTrustResourceUrl(value: string): SafeResourceUrl;
}Usage Examples:
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
import { Component } from "@angular/core";
@Component({
selector: 'app-content',
template: `
<div [innerHTML]="sanitizedHtml"></div>
<div [style.background-image]="trustedUrl"></div>
`
})
export class ContentComponent {
sanitizedHtml: SafeHtml;
trustedUrl: SafeStyle;
constructor(private sanitizer: DomSanitizer) {
// Sanitize user-provided HTML
const userHtml = '<script>alert("xss")</script><p>Safe content</p>';
this.sanitizedHtml = this.sanitizer.sanitize(SecurityContext.HTML, userHtml);
// Trust a known safe URL
const imageUrl = 'url(https://trusted-cdn.com/image.jpg)';
this.trustedUrl = this.sanitizer.bypassSecurityTrustStyle(imageUrl);
}
// Method to safely set dynamic HTML
setSafeHtml(html: string): SafeHtml {
// Only bypass security for content you trust completely
if (this.isContentTrusted(html)) {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
// Let Angular sanitize untrusted content
return this.sanitizer.sanitize(SecurityContext.HTML, html);
}
private isContentTrusted(html: string): boolean {
// Implement your trust validation logic
return html.startsWith('<p>') && !html.includes('<script>');
}
}Marker interfaces that indicate values have been validated as safe for specific security contexts.
/**
* Marker interface for a value that's safe to use in a particular context.
*/
interface SafeValue {}
/**
* Marker interface for a value that's safe to use as HTML.
*/
interface SafeHtml extends SafeValue {}
/**
* Marker interface for a value that's safe to use as style (CSS).
*/
interface SafeStyle extends SafeValue {}
/**
* Marker interface for a value that's safe to use as JavaScript.
*/
interface SafeScript extends SafeValue {}
/**
* Marker interface for a value that's safe to use as a URL linking to a document.
*/
interface SafeUrl extends SafeValue {}
/**
* Marker interface for a value that's safe to use as a URL to load executable code from.
*/
interface SafeResourceUrl extends SafeValue {}Usage Examples:
import { DomSanitizer, SafeHtml, SafeUrl } from "@angular/platform-browser";
@Injectable()
export class SafeContentService {
constructor(private sanitizer: DomSanitizer) {}
// Create safe HTML for trusted templates
createSafeHtml(template: string, data: any): SafeHtml {
const processedTemplate = this.processTemplate(template, data);
return this.sanitizer.bypassSecurityTrustHtml(processedTemplate);
}
// Create safe URLs for trusted sources
createSafeUrl(url: string): SafeUrl {
if (this.isUrlTrusted(url)) {
return this.sanitizer.bypassSecurityTrustUrl(url);
}
throw new Error('Untrusted URL');
}
private isUrlTrusted(url: string): boolean {
const trustedDomains = ['https://api.myapp.com', 'https://cdn.myapp.com'];
return trustedDomains.some(domain => url.startsWith(domain));
}
private processTemplate(template: string, data: any): string {
// Process template with trusted data
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return data[key] || '';
});
}
}Security contexts define how values are validated and sanitized based on their intended use.
/**
* Security context in which to sanitize a value.
* Defines how Angular should sanitize values in different DOM contexts.
*/
enum SecurityContext {
NONE = 0,
HTML = 1,
STYLE = 2,
SCRIPT = 3,
URL = 4,
RESOURCE_URL = 5
}Security Context Usage:
import { SecurityContext, DomSanitizer } from "@angular/platform-browser";
@Injectable()
export class SecurityService {
constructor(private sanitizer: DomSanitizer) {}
sanitizeForContext(value: string, context: SecurityContext): string | null {
switch (context) {
case SecurityContext.HTML:
// Removes script tags, javascript: URLs, etc.
return this.sanitizer.sanitize(SecurityContext.HTML, value);
case SecurityContext.STYLE:
// Validates CSS values
return this.sanitizer.sanitize(SecurityContext.STYLE, value);
case SecurityContext.URL:
// Validates URLs, blocks javascript: URLs
return this.sanitizer.sanitize(SecurityContext.URL, value);
case SecurityContext.SCRIPT:
// Always throws error - scripts cannot be sanitized
return this.sanitizer.sanitize(SecurityContext.SCRIPT, value);
case SecurityContext.RESOURCE_URL:
// Always throws error for untrusted resource URLs
return this.sanitizer.sanitize(SecurityContext.RESOURCE_URL, value);
default:
return value;
}
}
}Best Practices:
// ❌ DON'T: Bypass security for user input
const userInput = getUserInput();
const dangerousHtml = sanitizer.bypassSecurityTrustHtml(userInput);
// ✅ DO: Sanitize user input
const userInput = getUserInput();
const safeHtml = sanitizer.sanitize(SecurityContext.HTML, userInput);
// ✅ DO: Only bypass for content you control
const trustedTemplate = '<div class="highlight">{{content}}</div>';
const safeTemplate = sanitizer.bypassSecurityTrustHtml(trustedTemplate);
// ✅ DO: Validate before bypassing
const url = getImageUrl();
if (url.startsWith('https://trusted-cdn.com/')) {
const safeUrl = sanitizer.bypassSecurityTrustUrl(url);
}