Application Security

Angular Application Security Checklist

A practical security checklist for Angular applications covering XSS prevention, dependency management, and secure configuration.

Michael
Staff Security Engineer
4 min read

Angular has strong built-in security features, arguably stronger than any other major frontend framework. Its template compiler sanitizes by default, its DomSanitizer provides explicit escape hatches, and its strict mode catches many security issues at build time. But defaults only help if you understand them and avoid circumventing them.

Angular's Built-In XSS Protection

Angular's template engine automatically sanitizes values interpolated into templates:

<!-- Angular escapes this automatically -->
<p>{{ userInput }}</p>

Angular sanitizes values based on context. It handles HTML, URLs, styles, and resource URLs differently. This contextual escaping is more sophisticated than React's approach.

Bypass Functions: Handle With Care

Angular provides explicit bypass methods in DomSanitizer:

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

@Component({
  template: `<div [innerHTML]="trustedHtml"></div>`
})
export class RichContentComponent {
  trustedHtml: SafeHtml;

  constructor(private sanitizer: DomSanitizer) {
    // DANGEROUS: bypasses Angular's sanitization
    this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml(userContent);
  }
}

Every bypassSecurityTrust* call is a potential XSS vulnerability. Audit your codebase for these calls regularly. If you must render user HTML, sanitize it with DOMPurify before bypassing Angular's sanitizer:

import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(userContent);
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml(clean);

Template Injection

Angular compiles templates at build time (Ahead-of-Time compilation). This prevents template injection attacks where an attacker provides a malicious template string. Always use AOT compilation in production:

ng build --configuration production

Never use Compiler or dynamic template compilation with user-provided strings. The JIT compiler should never be in your production bundle.

HTTP Security

HttpClient and XSRF Protection

Angular's HttpClient includes built-in XSRF (CSRF) protection:

// app.module.ts
import { HttpClientXsrfModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule,
    HttpClientXsrfModule.withOptions({
      cookieName: 'XSRF-TOKEN',
      headerName: 'X-XSRF-TOKEN',
    }),
  ],
})
export class AppModule {}

This automatically reads the XSRF token from a cookie and adds it as a header on mutating requests.

Interceptors for Security Headers

Use HTTP interceptors to add security headers to every request:

@Injectable()
export class SecurityInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const secureReq = req.clone({
      setHeaders: {
        'X-Requested-With': 'XMLHttpRequest',
      },
    });
    return next.handle(secureReq);
  }
}

Content Security Policy

Angular applications need careful CSP configuration. AOT-compiled Angular does not use eval(), so you can avoid 'unsafe-eval':

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self' https://api.yourapp.com;

If you use Angular's built-in styles (component styles), you may need 'unsafe-inline' for style-src. Work toward nonce-based CSP to eliminate this.

Route Guards Are Not Security

Angular route guards (CanActivate, CanLoad) are UX features, not security controls:

@Injectable()
export class AdminGuard implements CanActivate {
  canActivate(): boolean {
    return this.authService.isAdmin();
  }
}

A determined attacker can bypass client-side route guards. Every protected resource must be authorized on the server.

Strict Mode

Angular's strict mode (--strict flag when creating a project) enables stricter TypeScript and Angular compiler checks:

ng new my-app --strict

This enables:

  • strictTemplates - Catches template type errors
  • strictInjectionParameters - Requires explicit injection metadata
  • noImplicitAny - No implicit any types

Use strict mode for all new projects. Migrate existing projects incrementally.

Dependency Security

Angular projects use npm. The framework has a large dependency tree (RxJS, Zone.js, TypeScript, and numerous @angular/* packages). Keep Angular updated. The Angular team releases security patches regularly, and Angular versions reach end-of-life on a predictable schedule.

# Check for outdated Angular packages
ng update

# Audit dependencies
npm audit --audit-level=high

Generate an SBOM:

npx @cyclonedx/cyclonedx-npm --output-file sbom.json

Security Checklist

  • [ ] AOT compilation enabled in production
  • [ ] No bypassSecurityTrust* calls with unsanitized input
  • [ ] CSP headers configured without 'unsafe-eval'
  • [ ] XSRF protection configured in HttpClient
  • [ ] All API endpoints validate auth server-side
  • [ ] Strict mode enabled
  • [ ] npm audit running in CI
  • [ ] Dependencies pinned in package-lock.json
  • [ ] No secrets in environment files shipped to client
  • [ ] Route guards backed by server-side authorization

How Safeguard.sh Helps

Safeguard.sh tracks the full dependency tree of your Angular applications, including the Angular framework packages themselves. It monitors for CVEs across your @angular/* packages, RxJS, Zone.js, and every other dependency. For organizations with multiple Angular applications, Safeguard.sh provides a consolidated view showing which apps are on supported Angular versions and which need framework upgrades, helping your team stay ahead of both dependency vulnerabilities and framework end-of-life.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.