Skip to content

XSS Protection for APIs — Complete Cross-Site Scripting Guide

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about XSS Protection for APIs. We cover key concepts, practical examples, and best practices to help you master this topic.

Cross-Site Scripting (XSS) allows attackers to inject malicious scripts into web pages viewed by other users. For APIs, XSS occurs when user-supplied data is returned without proper encoding.

What You'll Learn

You'll learn the three types of XSS (reflected, stored, DOM-based) and how to prevent them through API-level defenses.

Why It Matters

XSS is the second most common web vulnerability. It enables Session Hijacking, credential theft, defacement, and malware distribution. An XSS vulnerability in your API affects every user of your application.

Real-World Use

A forum API returns user posts without encoding. An attacker posts a comment containing a script that steals session cookies. Every user who views the page unknowingly sends their session token to the attacker.

flowchart LR
    subgraph Stored XSS
        A[Attacker] --> B[Post Malicious Comment]
        B --> C[Database]
        C --> D[API Returns Unencoded]
        D --> E[User Browser Executes Script]
        E --> F[Cookies Sent to Attacker]
    end
    subgraph Prevention
        G[Input Sanitization]
        H[Output Encoding]
        I[CSP Headers]
        J[HttpOnly Cookies]
    end

Teacher's Mindset

XSS is like a library where a vandal wrote a fake notice that says "click here to reset your password" on a real book page. The library should check all content before displaying it (sanitization) and display it in a protective sleeve (encoding).

Preventing XSS in APIs

from flask import Flask, request, jsonify
import bleach
import html

app = Flask(__name__)

ALLOWED_TAGS = ["b", "i", "em", "strong", "p", "br"]
ALLOWED_ATTRS = {}

@app.route("/api/posts", methods=["POST"])
def create_post():
    data = request.json
    raw_content = data.get("content", "")

    safe_content = bleach.clean(
        raw_content,
        tags=ALLOWED_TAGS,
        attributes=ALLOWED_ATTRS,
        strip=True,
        strip_comments=True
    )

    return jsonify({
        "content": safe_content,
        "sanitized": raw_content != safe_content
    })
# Set security headers for XSS prevention
@app.after_request
def add_security_headers(response):
    response.headers["X-XSS-Protection"] = "1; mode=block"
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["Content-Security-Policy"] = (
        "default-src 'self'; "
        "script-src 'self'; "
        "style-src 'self' 'unsafe-inline'"
    )
    return response
# Reflected XSS prevention
import urllib.parse

def validate_redirect_url(url):
    parsed = urllib.parse.urlparse(url)
    if parsed.netloc and parsed.netloc != "example.com":
        raise ValueError("External redirect blocked")
    return html.escape(url)

@app.route("/api/redirect")
def redirect():
    target = request.args.get("url", "")
    try:
        safe_url = validate_redirect_url(target)
        return jsonify({"redirect": safe_url})
    except ValueError as e:
        return jsonify({"error": str(e)}), 400

Common Mistakes

Mistake Why It's Wrong Fix
Returning raw HTML from API Every XSS payload executes in the browser Encode all user-controlled output
Setting Content-Type to text/html Browser interprets response as HTML Always use application/json for APIs
Trusting CORS to prevent XSS CORS does not prevent XSS, only cross-origin reads CSP + output encoding are necessary
Not encoding JSON callback names JSONP callback injection Avoid JSONP, use CORS instead
Whitelisting