Angular Security Implementation

How to Secure an Angular Application from XSS and CSRF

Let me walk you through my approach to implementing security in Angular applications. I’ll focus on the most critical vulnerabilities: XSS and CSRF.

First, let’s understand what we’re protecting against:

  1. XSS (Cross-Site Scripting)
  2. CSRF (Cross-Site Request Forgery)
  3. Data Injection
  4. Unauthorized Access

Here’s how I implement comprehensive security:

// 1. XSS Protection Service
@Injectable({ providedIn: 'root' })
export class SecurityService {
  private sanitizer = inject(DomSanitizer);
  
  sanitizeHtml(content: string): SafeHtml {
    // First, validate content structure
    if (this.containsScriptTags(content)) {
      console.warn('Attempted XSS injection detected');
      return '';
    }
    
    return this.sanitizer.bypassSecurityTrustHtml(
      this.cleanHtml(content)
    );
  }
  
  sanitizeUrl(url: string): SafeUrl {
    if (!this.isValidUrl(url)) {
      console.warn('Invalid URL detected');
      return '';
    }
    
    return this.sanitizer.bypassSecurityTrustUrl(url);
  }
  
  private containsScriptTags(content: string): boolean {
    return /<script\b[^>]*>([\s\S]*?)<\/script>/gi
      .test(content);
  }
  
  private cleanHtml(html: string): string {
    return html.replace(
      /<script\b[^>]*>([\s\S]*?)<\/script>/gi,
      ''
    );
  }
  
  private isValidUrl(url: string): boolean {
    try {
      const parsed = new URL(url);
      return ['http:', 'https:'].includes(parsed.protocol);
    } catch {
      return false;
    }
  }
}

// 2. CSRF Protection
@Injectable()
export class CsrfInterceptor implements HttpInterceptor {
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    // Skip for GET requests and non-relative URLs
    if (req.method === 'GET' || /^https?:\/\//i.test(req.url)) {
      return next.handle(req);
    }
    
    // Add CSRF token
    const token = this.getToken();
    if (token) {
      req = req.clone({
        headers: req.headers.set(
          'X-CSRF-TOKEN',
          token
        )
      });
    }
    
    return next.handle(req).pipe(
      catchError(error => {
        if (error.status === 403) {
          // Handle CSRF token validation failure
          this.refreshToken();
        }
        throw error;
      })
    );
  }
  
  private getToken(): string {
    return document.cookie.match(
      /XSRF-TOKEN=(.*?);/
    )?.[1] || '';
  }
  
  private refreshToken() {
    // Implement token refresh logic
  }
}

// 3. Secure Component Implementation
@Component({
  selector: 'app-secure-content',
  template: `
    <!-- Safe HTML binding -->
    <div [innerHTML]="sanitizedContent()"></div>
    
    <!-- Safe URL binding -->
    <iframe [src]="sanitizedUrl()" />
    
    <!-- Safe attribute binding -->
    <div [attr.data-content]="sanitizedAttr()"></div>
    
    <!-- Form with CSRF protection -->
    <form #form="ngForm" (ngSubmit)="onSubmit()">
      <input 
        type="hidden"
        [value]="csrfToken"
      />
      <input 
        name="data"
        [(ngModel)]="data"
        required
      />
      <button type="submit">Submit</button>
    </form>
  `
})
export class SecureComponent {
  private security = inject(SecurityService);
  
  // Use signals for reactive updates
  private content = signal<string>('');
  private url = signal<string>('');
  
  // Computed safe values
  sanitizedContent = computed(() => 
    this.security.sanitizeHtml(this.content())
  );
  
  sanitizedUrl = computed(() => 
    this.security.sanitizeUrl(this.url())
  );
  
  sanitizedAttr = computed(() => 
    encodeURIComponent(this.content())
  );
  
  onSubmit() {
    // Form submission with CSRF token
  }
}

// 4. Content Security Policy
@Injectable({ providedIn: 'root' })
export class CspService {
  private policies = {
    'default-src': ["'self'"],
    'script-src': ["'self'", "'strict-dynamic'"],
    'style-src': ["'self'", "'unsafe-inline'"],
    'img-src': ["'self'", 'data:', 'https:'],
    'connect-src': ["'self'", 'https://api.example.com'],
    'frame-src': ["'none'"],
    'object-src': ["'none'"],
    'base-uri': ["'self'"]
  };
  
  getCspHeader(): string {
    return Object.entries(this.policies)
      .map(([key, values]) => 
        `${key} ${values.join(' ')}`
      )
      .join('; ');
  }
}

Real-world implementation example:

// Secure API Service
@Injectable({ providedIn: 'root' })
export class SecureApiService {
  private security = inject(SecurityService);
  private http = inject(HttpClient);
  
  submitData(data: any) {
    // 1. Sanitize input
    const cleanData = this.sanitizeInput(data);
    
    // 2. Add security headers
    const headers = this.getSecurityHeaders();
    
    // 3. Make secure request
    return this.http.post('/api/data', cleanData, { headers })
      .pipe(
        // 4. Sanitize response
        map(response => this.sanitizeOutput(response)),
        // 5. Handle security errors
        catchError(error => this.handleSecurityError(error))
      );
  }
  
  private sanitizeInput(data: any) {
    // Implement input sanitization
    return data;
  }
  
  private getSecurityHeaders(): HttpHeaders {
    return new HttpHeaders({
      'Content-Type': 'application/json',
      'X-CSRF-TOKEN': this.getCsrfToken(),
      'X-Frame-Options': 'DENY',
      'X-Content-Type-Options': 'nosniff'
    });
  }
}

Key points I emphasize in interviews:

  1. Built-in Angular Security

    • Automatic XSS protection
    • Context-aware sanitization
    • Template syntax security
  2. Additional Protections

    • CSRF tokens
    • Content Security Policy
    • Secure headers
    • Input validation
  3. Best Practices

    • Always sanitize user input
    • Use HttpOnly cookies
    • Implement proper CORS
    • Regular security audits
  4. Common Pitfalls

    • Bypassing built-in sanitization
    • Insecure localStorage usage
    • Missing CSRF protection
    • Weak input validation

This approach has helped me build secure Angular applications that protect against common vulnerabilities while maintaining good performance and user experience.