Angular helps you stay safe by default, but it cannot protect you from unsafe choices. This lesson covers the main risks in a browser app and what to do about them. Remember the golden rule: everything that runs in the browser can be read and changed by the user, so real security always lives on the server.
What you will learn
- How Angular's built-in protection against XSS works, and how people break it
- How to treat tokens, cookies and CSRF
- Headers and habits that reduce risk
1. Cross-site scripting (XSS)
XSS means an attacker gets their JavaScript to run on your page, for example through a comment field. Angular treats every value as untrusted and escapes it:
<!-- Safe: Angular escapes the text, so a <script> in userComment is shown as text -->
<p>{{ userComment }}</p>
<!-- Sanitised: dangerous parts such as script tags and event handlers are removed -->
<div [innerHTML]="userHtml"></div>
You can switch the protection off. Never do it with user data:
// File: src/app/security/unsafe-example.ts
import { Component, inject } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'app-unsafe-example',
template: `<div [innerHTML]="html"></div>`,
})
export class UnsafeExample {
private readonly sanitizer = inject(DomSanitizer);
// DANGEROUS if the string can come from a user or an API you do not control.
protected html: SafeHtml = this.sanitizer.bypassSecurityTrustHtml('<b>trusted by you</b>');
}
Rules:
- Avoid
bypassSecurityTrust...methods. If a reviewer sees one, it must have a written reason. - Do not build HTML strings and assign them to
innerHTML,outerHTMLordocument.write. - Do not use
ElementRef.nativeElement.innerHTML. Bind with templates instead. - Never put user input into
[href]or[src]without checking. Angular blocksjavascript:URLs, which is one more reason not to bypass it. - If you must show rich text (for example blog content), sanitise it on the server with a maintained library, store a safe format such as Markdown or a block structure, and keep Angular's sanitiser on.
2. Tokens and cookies
Where to keep a login token is a trade-off:
- `localStorage` / `sessionStorage`: Risk: Readable by any script on the page, so one XSS bug leaks it; Notes: Common, simple, and the least safe
- In-memory variable: Risk: Lost on refresh, hard to steal with XSS after the fact; Notes: Combine with a refresh cookie
- **HttpOnly, Secure, SameSite cookie**: Risk: JavaScript cannot read it, so XSS cannot steal it directly; Notes: Needs CSRF protection
For most apps, the safest setup is a session or refresh token in an HttpOnly, Secure, SameSite=Lax (or Strict) cookie set by your server, with short-lived access tokens kept in memory. Whatever you choose, use HTTPS everywhere, short lifetimes, and make logout revoke the session on the server.
3. CSRF (cross-site request forgery)
If you use cookies for auth, a malicious site can make the browser send requests to your API with those cookies. Defend with:
SameSitecookies,- a CSRF token the server checks on every state-changing request (POST, PUT, PATCH, DELETE).
Angular's HttpClient has built-in support for the common double-submit pattern: it reads a cookie named XSRF-TOKEN and sends its value in an X-XSRF-TOKEN header for mutating requests to the same origin. You can configure it:
// File: src/app/app.config.ts (excerpt)
import { provideHttpClient, withFetch, withXsrfConfiguration } from '@angular/common/http';
// providers: [
// provideHttpClient(
// withFetch(),
// withXsrfConfiguration({ cookieName: 'XSRF-TOKEN', headerName: 'X-XSRF-TOKEN' }),
// ),
// ]
The server must set the cookie and verify the header. If you authenticate with an Authorization: Bearer header instead of cookies, classic CSRF does not apply, but XSS protection matters even more.
4. Never trust the client
- Guards and hidden buttons are usability, not security. Check permissions on every API call on the server.
- Validate and sanitise all input on the server, even if the Angular form already validated it.
- Do not put secrets in Angular code or environment files. Everything shipped to the browser is public. API keys that must stay secret belong on the server.
- Do not return more data than the user may see and hope the UI hides it.
5. Content Security Policy (CSP) and other headers
A CSP tells the browser which scripts it may run, limiting the damage of an XSS bug. Set headers on your web server or CDN:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Start in report-only mode (Content-Security-Policy-Report-Only) to see what would break. The Angular build can also produce a strict, hash-based CSP (the security.autoCsp option, currently marked experimental), so check the current documentation before relying on it. Use frame-ancestors 'none' to prevent clickjacking.
6. Dependencies and build
- Run
npm auditand keep Angular and packages updated.ng updatehandles Angular upgrades. - Commit lock files, use
npm ciin CI, and review new dependencies. - Do not paste unknown scripts into
index.html. Third-party scripts have the same power as your own code. - Turn on source-map restrictions in production if you do not want to expose original code (it is not a secret either way).
7. Security checklist
- No
bypassSecurityTrust...with user data. - No
innerHTMLbuilt by hand. - HTTPS and security headers in production.
- Tokens in HttpOnly cookies or memory, short lifetimes.
- CSRF protection if you use cookies.
- Server-side authorisation and validation on every request.
- No secrets in the client bundle.
- Dependencies updated and audited.
Try it yourself
- Search your project for
bypassSecurityTrustandinnerHTML. Justify or remove each. - Check your site's headers with your browser's Network tab, then add the missing ones.
- Write down where your app stores its token and what happens if an XSS bug appears.
Quick check
- Why is
localStoragerisky for tokens? - When do you need CSRF protection?
- Why are route guards not security?
Next, automate quality checks with CI/CD. The official security guide is required reading.