Audit and exploit missing HTTP security headers using a live web API from Kali Linux:
Header audit β use curl to enumerate exactly which security headers are missing from an unprotected API (score: 0/7)
Reflected XSS amplified by missing CSP β send a <script> tag in a query parameter; with no Content-Security-Policy, the browser would execute it
Clickjacking risk from missing X-Frame-Options β demonstrate how the app can be embedded in a malicious iframe
MIME sniffing via missing X-Content-Type-Options β upload polyglot content that a browser would execute as a different type
Secure endpoint comparison β audit the protected version (score: 7/7) and confirm every header is present and correct
Implement the full header set β write a Flask middleware that adds all 7 headers globally
Background
Security headers are the cheapest, highest-ROI security controls available β a one-time server configuration change that mitigates entire vulnerability classes at the browser level.
Real-world impact of missing headers:
Missing CSP β XSS escalation: The 2018 British Airways breach ($228M fine, 500,000 customers) began with a skimming script injected into their payment page. A strong CSP blocking script-src 'self' would have prevented the injected script from executing.
Missing X-Frame-Options β Clickjacking: In 2009, Adobe Flash settings pages were clickjacked via invisible iframes, allowing attackers to silently enable webcam access. X-Frame-Options: DENY would have blocked this.
Missing HSTS β SSL strip: Attackers on the same network (coffee shop Wi-Fi, hotel) can downgrade HTTPS to HTTP before the first connection. HSTS tells the browser to always use HTTPS, preventing the downgrade.
Missing X-Content-Type-Options β MIME sniffing: A file uploaded as text/plain but containing HTML gets rendered as HTML by IE/Chrome if nosniff is absent β XSS via file uploads.
Missing Referrer-Policy β data leakage: Without this header, the browser sends the full URL (including query params with PII) in the Referer header to third-party analytics/CDN providers.
π‘ CSP (Content-Security-Policy) is the strongest XSS mitigation available. With script-src 'self', even if an attacker successfully injects a <script> tag, the browser refuses to execute it β the tag renders as visible text. Think of CSP as a whitelist for what your page is allowed to do. It's a second layer of defence: even if injection happens, execution is blocked.
Step 5: Clickjacking via Missing X-Frame-Options
πΈ Verified Output:
Step 6: HSTS β Preventing SSL Strip Attacks
πΈ Verified Output:
Step 7: Implement a Flask Security Header Middleware
echo "=== Missing CSP: XSS payload reflected without protection ==="
# The search parameter is reflected directly in the JSON response
# Without CSP, if this were HTML-rendered, the script tag would execute
curl -s "$TARGET/api/products?q=<script>alert(document.cookie)</script>"
echo ""
python3 << 'EOF'
# In a real HTML endpoint (not JSON), this is how reflected XSS works:
# 1. Attacker crafts URL: https://shop.com/products?q=<script>alert(1)</script>
# 2. Victim clicks the link
# 3. Server reflects the query param into HTML without encoding
# 4. Without CSP, the browser executes the script
# 5. With CSP "script-src 'self'", inline/reflected scripts are BLOCKED
payload = "<script>alert(document.cookie)</script>"
html_no_csp = f"""
<html>
<body>
<h1>Search results for: {payload}</h1>
<!-- Without CSP: script executes, steals cookie -->
<!-- With CSP (script-src 'self'): browser blocks inline script -->
</body>
</html>"""
print("What the page would render without CSP:")
print(html_no_csp[:300])
print()
print("CSP header that blocks this:")
print(" Content-Security-Policy: default-src 'self'; script-src 'self'")
print(" Effect: browser refuses to execute ANY inline script")
print(" XSS payload rendered as text, not code")
EOF
echo "=== HSTS: preventing HTTPS downgrade (SSL strip) ==="
python3 << 'EOF'
# Without HSTS: SSL strip attack
# 1. User on coffee shop Wi-Fi navigates to http://bank.com
# 2. Attacker intercepts and returns HTTP version (removes HTTPS redirect)
# 3. User's browser talks plain HTTP β attacker reads everything
# 4. Attacker talks HTTPS to the real bank β proxies all traffic
# With HSTS:
# 1. First time user visits https://bank.com, browser receives:
# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# 2. Browser stores "always use HTTPS for bank.com for next year"
# 3. Next visit: browser forces HTTPS BEFORE making any request
# 4. SSL strip attack impossible β no plain HTTP request ever sent
hsts_analysis = {
"max-age=31536000": "Browser remembers HTTPS requirement for 1 year",
"includeSubDomains": "Applies to all subdomains (api.bank.com, mail.bank.com)",
"preload": "Browser vendor pre-bakes the HTTPS requirement (ships with browser)"
}
print("HSTS directive breakdown:")
for directive, meaning in hsts_analysis.items():
print(f" {directive:<30} β {meaning}")
print()
print("From secure endpoint:")
EOF
curl -s -I $TARGET/api/products-secure | grep -i "Strict-Transport"
HSTS directive breakdown:
max-age=31536000 β Browser remembers HTTPS for 1 year
includeSubDomains β Applies to all subdomains
preload β Pre-baked into browser vendor list
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload