API Security Training

Exercise: The Secure Header

In this activity, you will configure Content Security Policy (CSP) headers to prevent unauthorized scripts from executing.

Scenario: Your API is returning a webpage that has a Cross-Site Scripting (XSS) bug. We cannot fix the code bug right now, so we must block the attack at the Browser Level.

An attacker is trying to load: <script src="http://evil.com/hack.js">.

Your Task

Update the get_security_headers function:

  1. Add a new key: "Content-Security-Policy".
  2. Set the value to: "default-src 'self'".
  3. This instructs the browser to only trust scripts coming from your own domain.

Why Headers?

Modern security is often about "Defense in Depth". Even if your code has bugs, good headers can prevent the bugs from being exploited.

PYTHON EDITOR (middleware.py)
1
BROWSER CONSOLE SIMULATION
Waiting for server headers...
from js import document, window import sys def setup(): loading = document.getElementById("loading") if loading: loading.style.display = "none" setup() class ConsoleOutput: def write(self, text): if text.strip(): log(text.strip(), "normal") def flush(self): pass sys.stdout = ConsoleOutput() sys.stderr = ConsoleOutput() def log(message, type="normal"): console = document.getElementById("console-output") if type == "clear": console.innerHTML = "" return color = "#ffffff" if type == "pass": color = "#2ecc71" if type == "fail": color = "#e74c3c" if type == "info": color = "#3498db" if type == "system": color = "#d4d4d4" safe_message = message.replace("<", "<").replace(">", ">") console.innerHTML += f'
{safe_message}
' console.scrollTop = console.scrollHeight def run_tests(*args): log("", "clear") log("[SYSTEM] Inspecting HTTP Headers...", "system") user_code = document.getElementById("code-editor").value test_env = {} window.submissionStatus = "INCOMPLETE / FAILED" try: exec(user_code, test_env) if "get_security_headers" not in test_env: log("[!] Error: Function 'get_security_headers' not found.", "fail") return headers = test_env["get_security_headers"]() if not isinstance(headers, dict): log("[!] Error: Function must return a dictionary.", "fail") return headers_lower = {k.lower(): v for k, v in headers.items()} # TEST 1: Check for CSP Key log("--- TEST 1: Header Presence ---", "system") if "content-security-policy" not in headers_lower: log(" -> FAIL: CSP Header missing entirely.", "fail") log(" [Browser] Script from 'evil.com' executed!", "fail") window.submissionStatus = "FAILED - Missing CSP" return else: log(" -> PASS: CSP Header found.", "pass") # TEST 2: Check Value log("--- TEST 2: Policy Strength ---", "system") val = headers_lower["content-security-policy"] if "default-src 'self'" in val: log(f" -> PASS: Policy '{val}' allows self only.", "pass") log(" [Browser] Blocked loading resource from 'http://evil.com/hack.js'", "pass") log(" SUCCESS: Attack Mitigated.", "pass") window.submissionStatus = "PASSED - Secure" elif "'none'" in val: log(" -> PASS: Strict 'none' policy detected.", "pass") window.submissionStatus = "PASSED - Secure" else: log(f" -> FAIL: Policy '{val}' is too weak or incorrect.", "fail") log(" [Browser] 'evil.com' might still be allowed.", "fail") window.submissionStatus = "FAILED - Weak Policy" except Exception as e: log(f"[!] Runtime Error: {e}", "fail") window.submissionStatus = "ERROR - Syntax/Runtime"