GuardAPI Logo
GuardAPI

Fix XSS in API Responses in Sanic

XSS in Sanic APIs typically stems from improper use of response types or failing to sanitize user-controlled data reflected in the output. While Sanic is built for speed, developers often shoot themselves in the foot by returning raw strings with 'text/html' content types or using 'response.html' without escaping, allowing attackers to inject malicious scripts into the DOM.

The Vulnerable Pattern

from sanic import Sanic, response

app = Sanic(‘VulnerableAPI’)

@app.get(‘/v1/greet’) async def vuln_handler(request): name = request.args.get(‘name’, ‘Guest’) # VULNERABLE: Direct reflection of input into HTML response return response.html(f’

Hello {name}

’)

@app.get(‘/v1/debug’) async def debug_handler(request): payload = request.args.get(‘data’) # VULNERABLE: Setting content_type to text/html for what should be JSON/Plain text return response.text(f’Debug info: {payload}’, content_type=‘text/html’)

The Secure Implementation

To kill XSS in Sanic, follow three rules: 1. Use 'response.json()' for all API endpoints. This forces the 'Content-Type: application/json' header, which instructs modern browsers not to interpret the body as HTML. 2. If you must return HTML, use 'markupsafe.escape' or a templating engine like Jinja2 that auto-escapes. 3. Hardening: Apply a global middleware to inject 'Content-Security-Policy' (CSP) and 'X-Content-Type-Options: nosniff' headers. This prevents the browser from 'sniffing' a JSON response and trying to render it as HTML if it contains script-like tags.

from sanic import Sanic, response
from markupsafe import escape

app = Sanic(‘SecureAPI’)

@app.get(‘/v1/greet’) async def secure_handler(request): name = request.args.get(‘name’, ‘Guest’) # SECURE: Explicitly escape user input before rendering return response.html(f’

Hello {escape(name)}

’)

@app.get(‘/v1/data’) async def json_handler(request): user_data = request.args.get(‘input’) # SECURE: Use response.json() which sets ‘application/json’ and prevents HTML parsing return response.json({‘status’: ‘ok’, ‘received’: user_data})

@app.middleware(‘response’) async def add_csp_headers(request, response): # SECURE: Implement Content-Security-Policy as a secondary defense response.headers[‘Content-Security-Policy’] = “default-src ‘self’; script-src ‘none’;”

System Alert • ID: 4877
Target: API Responses API
Potential Vulnerability

Your API Responses API might be exposed to XSS

74% of API Responses apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

Free Tier • No Credit Card • Instant Report

Verified by Ghost Labs Security Team

This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.