Security Mechanisms
XSS Protection
Content Security Policy (CSP) Configuration:
<!-- Set CSP meta tag in index.html -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://trusted.cdn.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https://*.example.com">DOM Sanitization Implementation:
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({...})
export class SafeContentComponent {
safeHtml: SafeHtml;
constructor(private sanitizer: DomSanitizer) {}
sanitizeContent(rawHtml: string) {
// Use DomSanitizer for sanitization
this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(rawHtml);
// Safer approach with custom sanitization logic
// this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(
// this.customSanitizer.sanitize(rawHtml)
// );
}
}XSS Protection Best Practices:
- Never use
innerHTMLto insert user input directly - Use Angular’s data binding syntax
{{ }}for automatic content escaping - Strictly use
DomSanitizerfor scenarios requiring HTML insertion - Avoid using
eval(),new Function(), or other dynamic code execution
CSRF Protection
HttpInterceptor for CSRF Protection:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class CsrfInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Exclude requests that don't require CSRF protection (e.g., GET, HEAD, OPTIONS)
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
return next.handle(req);
}
// Retrieve CSRF token (typically from cookie)
const csrfToken = this.getCookie('XSRF-TOKEN');
// Clone request and add CSRF header
if (csrfToken) {
const securedReq = req.clone({
headers: req.headers.set('X-XSRF-TOKEN', csrfToken)
});
return next.handle(securedReq);
}
return next.handle(req);
}
private getCookie(name: string): string | null {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
return null;
}
}
// Register interceptor in AppModule
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true }
]
})
export class AppModule {}CSRF Protection Configuration:
- Ensure the backend generates and sets the
XSRF-TOKENcookie - Frontend retrieves the token from the cookie and adds it to the request header
- Backend verifies that the token in the request header matches the cookie token
Route Guards and Permission Control
Role-Based Route Guard:
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class RoleGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
const requiredRoles = route.data['roles'] as string[];
const userRoles = this.authService.getUserRoles();
if (this.authService.isLoggedIn() && requiredRoles.some(role => userRoles.includes(role))) {
return true;
}
// Redirect to login or unauthorized page
this.router.navigate(['/unauthorized']);
return false;
}
}
// Use guard in route configuration
const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [RoleGuard],
data: { roles: ['admin', 'superadmin'] }
}
];Route Guard Types:
CanActivate: Determines if a route can be activatedCanDeactivate: Determines if a route can be deactivatedResolve: Fetches data before route activationCanLoad: Determines if a feature module can be loaded
Sensitive Data Encryption and Storage
Encrypted Storage for Sensitive Data:
import { Injectable } from '@angular/core';
import { CryptoService } from './crypto.service';
@Injectable({
providedIn: 'root'
})
export class SecureStorageService {
constructor(private cryptoService: CryptoService) {}
setItem(key: string, value: string): void {
const encryptedValue = this.cryptoService.encrypt(value);
localStorage.setItem(key, encryptedValue);
}
getItem(key: string): string | null {
const encryptedValue = localStorage.getItem(key);
if (!encryptedValue) return null;
return this.cryptoService.decrypt(encryptedValue);
}
removeItem(key: string): void {
localStorage.removeItem(key);
}
}
// Encryption service using Web Crypto API
@Injectable({
providedIn: 'root'
})
export class CryptoService {
private readonly algorithm = { name: 'AES-GCM', length: 256 };
private readonly key: CryptoKey;
constructor() {
this.initKey();
}
private async initKey(): Promise<void> {
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
new TextEncoder().encode('my-secret-key-32-chars-long-1234567890'),
this.algorithm,
false,
['encrypt', 'decrypt']
);
this.key = keyMaterial;
}
async encrypt(data: string): Promise<string> {
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(data);
const ciphertext = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
this.key,
encoded
);
return JSON.stringify({
iv: Array.from(iv),
ciphertext: Array.from(new Uint8Array(ciphertext))
});
}
async decrypt(encryptedData: string): Promise<string> {
const { iv, ciphertext } = JSON.parse(encryptedData);
const decrypted = await window.crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: new Uint8Array(iv) },
this.key,
new Uint8Array(ciphertext)
);
return new TextDecoder().decode(decrypted);
}
}Secure Storage Recommendations:
- Avoid storing sensitive data in localStorage/sessionStorage
- Use strong encryption when storage is necessary
- Consider using HttpOnly, Secure cookies for session tokens
- Implement automatic token refresh mechanisms
Security Best Practices
Input Validation Implementation:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-user-form',
template: `
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<input formControlName="username" placeholder="Username">
<div *ngIf="userForm.get('username')?.errors?.['required']">
Username is required
</div>
<div *ngIf="userForm.get('username')?.errors?.['minlength']">
Username must be at least 3 characters
</div>
<input formControlName="email" placeholder="Email">
<div *ngIf="userForm.get('email')?.errors?.['required']">
Email is required
</div>
<div *ngIf="userForm.get('email')?.errors?.['email']">
Please enter a valid email address
</div>
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
`
})
export class UserFormComponent {
userForm: FormGroup;
constructor(private fb: FormBuilder) {
this.userForm = this.fb.group({
username: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]]
});
}
onSubmit() {
if (this.userForm.valid) {
// Handle form submission
}
}
}Least Privilege Principle Implementation:
// Implement fine-grained permission checks in service
@Injectable({
providedIn: 'root'
})
export class PermissionService {
private userPermissions: string[] = [];
constructor(private authService: AuthService) {
this.loadUserPermissions();
}
private async loadUserPermissions(): Promise<void> {
this.userPermissions = await this.authService.getUserPermissions();
}
hasPermission(permission: string): boolean {
return this.userPermissions.includes(permission);
}
hasAnyPermission(permissions: string[]): boolean {
return permissions.some(p => this.hasPermission(p));
}
hasAllPermissions(permissions: string[]): boolean {
return permissions.every(p => this.hasPermission(p));
}
}
// Use permission service in component
@Component({...})
export class AdminPanelComponent {
canEditUsers = false;
canDeleteUsers = false;
constructor(private permissionService: PermissionService) {
this.canEditUsers = this.permissionService.hasPermission('user:edit');
this.canDeleteUsers = this.permissionService.hasPermission('user:delete');
}
}Permission Control
Role-Based Access Control (RBAC)
RBAC Model Implementation:
// Role and permission definitions
interface Role {
name: string;
permissions: string[];
}
const ROLES: Record<string, Role> = {
admin: {
name: 'admin',
permissions: ['user:create', 'user:read', 'user:update', 'user:delete']
},
editor: {
name: 'editor',
permissions: ['user:read', 'user:update']
},
viewer: {
name: 'viewer',
permissions: ['user:read']
}
};
// Permission check service
@Injectable({
providedIn: 'root'
})
export class RbacService {
private userRole: string;
constructor(private authService: AuthService) {
this.userRole = this.authService.getUserRole();
}
hasPermission(permission: string): boolean {
const role = ROLES[this.userRole];
return role?.permissions.includes(permission) || false;
}
hasAnyPermission(permissions: string[]): boolean {
return permissions.some(p => this.hasPermission(p));
}
hasAllPermissions(permissions: string[]): boolean {
return permissions.every(p => this.hasPermission(p));
}
}Dynamic Routing and Permission Management
Dynamic Route Generation:
// Route configuration
const APP_ROUTES: Route[] = [
{ path: 'login', component: LoginComponent },
{ path: 'unauthorized', component: UnauthorizedComponent },
// Other public routes...
];
// Dynamic route service
@Injectable({
providedIn: 'root'
})
export class DynamicRouteService {
constructor(
private router: Router,
private authService: AuthService
) {}
generateRoutes(): Promise<void> {
return this.authService.getUserRoles().then(roles => {
const allowedRoutes = this.getAllowedRoutes(roles);
this.addRoutesToRouter(allowedRoutes);
});
}
private getAllowedRoutes(roles: string[]): Route[] {
// Filter routes based on roles
const roleRoutes: Record<string, Route[]> = {
admin: [
{ path: 'admin', component: AdminComponent },
{ path: 'users', component: UsersComponent }
],
editor: [
{ path: 'editor', component: EditorComponent }
],
viewer: [
{ path: 'viewer', component: ViewerComponent }
]
};
// Merge all allowed routes
return roles.reduce((routes, role) => {
return routes.concat(roleRoutes[role] || []);
}, []);
}
private addRoutesToRouter(routes: Route[]): void {
// Get current route configuration
const currentConfig = this.router.config;
// Add dynamic routes
const updatedConfig = [...currentConfig, ...routes];
// Reset route configuration
this.router.resetConfig(updatedConfig);
}
}
// Initialize dynamic routes in AppComponent
@Component({...})
export class AppComponent implements OnInit {
constructor(
private dynamicRouteService: DynamicRouteService,
private router: Router
) {}
ngOnInit(): void {
this.dynamicRouteService.generateRoutes().then(() => {
// Check if current route is allowed
this.checkCurrentRouteAccess();
});
}
private checkCurrentRouteAccess(): void {
const currentRoute = this.router.url;
if (!this.isRouteAllowed(currentRoute)) {
this.router.navigate(['/unauthorized']);
}
}
private isRouteAllowed(route: string): boolean {
// Implement route access check logic
return true; // Simplified example
}
}Component and Directive Permission Control
Permission Directive Implementation:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { PermissionService } from './permission.service';
@Directive({
selector: '[appHasPermission]'
})
export class HasPermissionDirective {
@Input() appHasPermission: string | string[];
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private permissionService: PermissionService
) {}
ngOnInit(): void {
this.updateView();
}
private updateView(): void {
const permissions = Array.isArray(this.appHasPermission)
? this.appHasPermission
: [this.appHasPermission];
if (this.permissionService.hasAnyPermission(permissions)) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
// Usage example
@Component({
template: `
<button *appHasPermission="'user:create'">Create User</button>
<div *appHasPermission="['user:read', 'user:update']">
User Management Content
</div>
`
})
export class UserManagementComponent {}Permission Component Implementation:
import { Component, Input } from '@angular/core';
import { PermissionService } from './permission.service';
@Component({
selector: 'app-permission-wrapper',
template: `
<ng-container *ngIf="hasPermission">
<ng-content></ng-content>
</ng-container>
`
})
export class PermissionWrapperComponent {
@Input() permission: string | string[];
hasPermission = false;
constructor(private permissionService: PermissionService) {}
ngOnChanges(): void {
this.updatePermission();
}
private updatePermission(): void {
const permissions = Array.isArray(this.permission)
? this.permission
: [this.permission];
this.hasPermission = this.permissionService.hasAnyPermission(permissions);
}
}
// Usage example
@Component({
template: `
<app-permission-wrapper [permission]="'user:delete'">
<button>Delete User</button>
</app-permission-wrapper>
`
})
export class UserListComponent {}API Request Permission Verification
HTTP Interceptor for API Permission Verification:
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthService } from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// Clone request and add authorization header
const authReq = this.addAuthHeader(req);
return next.handle(authReq).pipe(
catchError((error: HttpErrorResponse) => {
// Handle 401 unauthorized error
if (error.status === 401) {
this.authService.handleUnauthorized();
}
// Handle 403 forbidden error
else if (error.status === 403) {
this.authService.handleForbidden();
}
return throwError(error);
})
);
}
private addAuthHeader(req: HttpRequest<any>): HttpRequest<any> {
const token = this.authService.getAccessToken();
if (token) {
return req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
}
return req;
}
}
// Register interceptor in AppModule
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]
})
export class AppModule {}API Permission Verification Strategies:
- JWT Token: Include JWT token in request headers
- OAuth2: Use access tokens for authentication
- API Keys: For service-to-service communication
- ABAC: Attribute-based access control
Permission Control Performance Optimization
Permission Caching Strategy:
@Injectable({
providedIn: 'root'
})
export class PermissionService {
private permissions: string[] = [];
private permissionsLoaded = false;
private permissionsSubject = new BehaviorSubject<string[]>([]);
permissions$ = this.permissionsSubject.asObservable();
constructor(private authService: AuthService) {}
async loadPermissions(): Promise<void> {
if (this.permissionsLoaded) {
return;
}
this.permissions = await this.authService.getUserPermissions();
this.permissionsSubject.next(this.permissions);
this.permissionsLoaded = true;
}
hasPermission(permission: string): boolean {
if (!this.permissionsLoaded) {
// Return false or throw error if permissions not loaded
return false;
}
return this.permissions.includes(permission);
}
// Other methods...
}
// Use permission service in route guard
@Injectable({
providedIn: 'root'
})
export class RoleGuard implements CanActivate {
constructor(
private permissionService: PermissionService,
private router: Router
) {}
async canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Promise<boolean> {
await this.permissionService.loadPermissions();
const requiredRoles = route.data['roles'] as string[];
const userRoles = this.permissionService.getUserRoles();
if (this.authService.isLoggedIn() && requiredRoles.some(role => userRoles.includes(role))) {
return true;
}
this.router.navigate(['/unauthorized']);
return false;
}
}Performance Optimization Strategies:
- Permission Preloading: Load permissions during application initialization
- Permission Caching: Avoid repeated permission data requests
- Lazy Permission Loading: Load permissions for specific features on demand
- Change Detection Optimization: Use OnPush change detection strategy
- Pure Pipes: Use pure pipes for permission-related display logic
By implementing these comprehensive security and permission controls, Angular applications can establish a secure and reliable user authentication and authorization system, effectively protecting application data and user privacy.



