Understanding XSS and CORS
A practical developer's guide to Cross-Site Scripting and Cross-Origin Resource Sharing
Web security is non-negotiable in modern frontend development. Two commonly discussed topics—XSS and CORS—often get confused. But they solve completely different problems.
Let's walk through both — what they are, how they work, and what you need to know to build secure frontend apps.
What is XSS?
XSS (Cross-Site Scripting) is a vulnerability that allows attackers to inject malicious JavaScript into your web pages. These scripts then run in the browser of anyone visiting your site — not on your server.
This can lead to:
- Credential theft
- Unauthorized actions
- Content spoofing
- Data leakage
Common Types of XSS
- Stored XSS: Script is stored in a database and shown to users later (e.g., in comments).
- Reflected XSS: Script is part of a URL, and the server reflects it back into the page.
- DOM-based XSS: Vulnerability is in your JavaScript code directly manipulating the DOM insecurely.
Preventing XSS
Input Validation and Sanitization
Validate both client-side and server-side. Always assume malicious intent.
Example – Client-Side Validation
function isValidInput(input) {
// Only allow alphanumeric characters and basic punctuation
const regex = /^[a-zA-Z0-9\s.,!?-]+$/;
return regex.test(input);
}
const userInput = document.getElementById("input").value;
if (!isValidInput(userInput)) {
alert("Invalid input.");
}Example – Server-Side (Express.js)
app.post("/submit", (req, res) => {
const userInput = req.body.userInput;
const regex = /^[a-zA-Z0-9\s.,!?-]+$/;
if (!regex.test(userInput)) {
return res.status(400).send("Invalid input.");
}
// Proceed with clean input
});Use Content Security Policy (CSP)
A strong CSP header helps block inline and third-party scripts.
Example – Set CSP in Express.js
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline';"
);
next();
});Sanitize with DOMPurify
For user-generated HTML content, libraries like DOMPurify are essential.
import DOMPurify from "dompurify";
const dirty = '<img src=x onerror=alert("XSS")>';
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href']
});
document.getElementById("output").innerHTML = clean;Use Frameworks that Escape by Default
React, Angular, and other modern frameworks escape user input automatically in most use cases.
Example – React JSX
const userInput = '<img src="x" onerror="alert(\'XSS!\')" />';
export default function App() {
return <div>{userInput}</div>; // Escaped by React
}Avoid eval() and Similar APIs
Never use eval(), Function(), or setTimeout() with dynamic strings.
Example – Don't Do This
const input = "alert('XSS')";
eval(input); // ❌ DangerousAdd Secure Headers
Add headers like X-Content-Type-Options to prevent MIME-type attacks.
app.use((req, res, next) => {
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-XSS-Protection", "1; mode=block");
next();
});What is CORS?
CORS (Cross-Origin Resource Sharing) is a browser security policy that prevents your frontend from accessing APIs on other origins without permission.
This protects users from malicious sites making unauthorized API calls on their behalf.
How CORS Works
Let's say your frontend is at https://myapp.com and your backend is at https://api.myapp.com. By default, the browser won't allow your frontend to call that backend — unless the backend says it's okay.
Common CORS Headers
| Header | Purpose |
|---|---|
Access-Control-Allow-Origin | Which domains are allowed |
Access-Control-Allow-Methods | Allowed HTTP methods (GET, POST, etc.) |
Access-Control-Allow-Headers | Allowed custom headers |
Access-Control-Allow-Credentials | Whether to send cookies/credentials |
Example – Set CORS in Express.js
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "https://myapp.com");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.setHeader("Access-Control-Allow-Credentials", "true");
next();
});What is a Preflight Request?
When your app uses a custom method (PUT, DELETE, etc.) or custom headers, browsers send an OPTIONS request first to check if it's allowed.
This is called a preflight request.
Example – Handling Preflight
app.options('/api/data', (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "https://myapp.com");
res.setHeader("Access-Control-Allow-Methods", "PUT, DELETE");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.status(200).end();
});XSS vs CORS – What's the Difference?
| XSS | CORS | |
|---|---|---|
| Type | Security vulnerability | Browser protection mechanism |
| Focus | Script injection in your site | Restricting cross-origin requests |
| Handled By | You (code + headers + frameworks) | The browser + server headers |
| Goal | Prevent malicious script execution | Prevent unauthorized API calls |
| Prevention | Input sanitization, CSP, escaping | Server-side CORS configuration |
Final Thoughts
- XSS is something you must actively prevent in your code.
- CORS is something you configure to allow or deny access across domains.
- They solve different problems but both play vital roles in frontend security.
Always sanitize, validate, and think like an attacker when writing frontend code.
Next: Core Web Vitals Guide - Learn the essential performance metrics that impact user experience and SEO.