Security Headers for APIs — Complete HSTS, CSP, X-Frame-Options Guide
In this tutorial, you will learn about Security Headers for APIs. We cover key concepts, practical examples, and best practices to help you master this topic.
Security headers are HTTP response headers that instruct browsers how to behave when interacting with your API. They provide critical protection against clickjacking, content sniffing, protocol downgrade, and data injection.
What You'll Learn
You'll learn about HSTS, CSP, X-Frame-Options, X-Content-Type-Options, and other security headers that protect API consumers.
Why It Matters
Security headers are free, easy to implement, and block entire categories of attacks. The absence of security headers is a common finding in penetration tests.
Real-World Use
An e-commerce API with strict CSP headers prevented a data skimming attack where injected scripts attempted to exfiltrate credit card numbers by blocking connections to unknown domains.
flowchart LR
A[API Response] --> B[Add Security Headers]
B --> C[HSTS: Force HTTPS]
B --> D[CSP: Block Inline Scripts]
B --> E[X-Frame-Options: Prevent Clickjacking]
B --> F[X-Content-Type-Options: No MIME Sniff]
B --> G[Referrer-Policy: Limit Referrer]
B --> H[Permissions-Policy: Restrict Features]
C --> I[Secure Browser Environment]
D --> I
E --> I
F --> I
G --> I
H --> I
Teacher's Mindset
Security headers are like a building's fire code requirements. Sprinklers (CSP), fire doors (X-Frame-Options), and emergency exits (HSTS) all work together to prevent disaster, and they cost very little to install.
Implementing Security Headers
from flask import Flask, jsonify
app = Flask(__name__)
@app.after_request
def add_security_headers(response):
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
response.headers["Content-Security-Policy"] = (
"default-src 'self'; "
"script-src 'self'; "
"style-src 'self' 'unsafe-inline'; "
"img-src 'self' data:; "
"font-src 'self'; "
"connect-src 'self'; "
"frame-ancestors 'none'; "
"form-action 'self'; "
"base-uri 'self'"
)
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()"
response.headers["Cross-Origin-Embedder-Policy"] = "require-corp"
response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
return response
# Conditional headers based on response type
@app.after_request
def conditional_security_headers(response):
if response.content_type == "application/json":
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Cache-Control"] = "no-store"
if request.path.startswith("/api/"):
response.headers["Strict-Transport-Security"] = (
"max-age=31536000; includeSubDomains"
)
response.headers["X-Frame-Options"] = "DENY"
return response
# CSP reporting
@app.after_request
def csp_with_reporting(response):
response.headers["Content-Security-Policy-Report-Only"] = (
"default-src 'self'; "
"script-src 'self'; "
"report-uri /api/csp-report"
)
return response
@app.route("/api/csp-report", methods=["POST"])
def csp_report():
report = request.json
print(f"CSP Violation: {report}")
return jsonify({"status": "logged"})
Common Mistakes
| Mistake | Why It's Wrong | Fix |
|---|---|---|
| HSTS max-age too short | Users vulnerable to downgrade attacks | Set max-age to at least 1 year (31536000) |
| CSP with 'unsafe-inline' everywhere | Defeats CSP's purpose | Use nonces or hashes for inline scripts |
| Missing X-Frame-Options | API docs rendered in iframes on malicious sites | Set DENY for APIs, SAMEORIGIN for trusted pages |
| No X-Content-Type-Options | Browser MIME sniffing can execute content as script | Always set nosniff |
| Not including subDomains in HSTS | Subdomains remain HTTP | Use includeSubDomains directive |
Practice Questions
- What attack does X-Frame-Options prevent?
- How does CSP block XSS Attacks?
- What is HSTS preloading?
- Why should you set X-Content-Type-Options: nosniff?
- What does Permissions-Policy control?
Challenge
Configure an API with all recommended security headers. Set up CSP reporting. Test with securityheaders.com or similar tools.
FAQ
Mini Project
Set up a Flask API with comprehensive security headers. Include CSP in Report-Only mode initially, collect violation reports, then switch to enforcement mode. Verify with a security header scanner.
What's Next
Learn about request size limiting to prevent denial of service through large payloads.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro