Web › Module 2 › Lesson 2
Security Headers and CSP
A02: CSP/HSTS/nosniff on via curl -I.
Visual · security_headers_csp
Security headers: curl -I. If that IP is a router login, STOP and CSP, HSTS, nosniff, frame controls.
Opening
Headers are not magic armor — they are browser instructions you forgot to send.
Under OWASP Top 10:2025 A02, missing or weak security headers are a common misconfiguration. Content-Security-Policy (CSP) limits where scripts and resources load from. Strict-Transport-Security (HSTS) tells browsers to prefer HTTPS on your host. X-Content-Type-Options: nosniff reduces MIME sniffing surprises. Frame-related headers (Lesson 3) fight clickjacking. Attackers do not “break CSP math” first — they abuse apps that never sent a policy. This is ORIGINAL Cyberlium teaching.
1. What headers are for: browser policy, not server authentication
Security response headers guide the browser. They do not replace access control, parameterized SQL, or authn. CSP reduces impact of some XSS classes when crafted carefully; it does not fix every injection. HSTS helps after a good first HTTPS visit (and preload is a separate decision). Attacker goal when headers are missing: easier XSS impact, MITM downgrade on YOUR users if you botched TLS posture, framing attacks (next lesson). Finding language: “No CSP; no HSTS; missing nosniff.” Fix language: “Add and test headers; start CSP in report-only if needed; never copy a random blog policy blindly.”
Command guide
A02 Misconfig — What headers are for
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8769"
Command — copy this
curl -sS -m 3 -D - "$SAFE/" -o /dev/null | head -n 25
2. CSP literacy: default-src, script-src, avoid unsafe-inline when you can
CSP is a allowlist of sources. A weak policy with unsafe-inline everywhere teaches little. A learning policy on YOUR lab: default-src 'self'; script-src 'self'. Nonces/hashes are the grown-up path for inline needs — name them; do not ship a BeEF bypass cookbook. Report-Only mode lets you watch violations without breaking prod on day one. Do not paste alert() payload packs as homework. Mechanism + policy on loopback is enough.
Command guide
A02 Misconfig — CSP literacy
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8769"
Command — copy this
curl -sS -m 3 -I "$SAFE/" | grep -i -E "content-security-policy|content-type" curl -sS -m 3 -I "$SAFE/broken" | grep -i content-security-policy curl -sS -m 3 -I "$SAFE/hard" | grep -i content-security-policy
3. HSTS and nosniff: short, high-value switches
Strict-Transport-Security with a sensible max-age (and includeSubDomains only when you mean it) tells browsers to use HTTPS for your host. Only send HSTS over HTTPS. X-Content-Type-Options: nosniff tells browsers not to guess content types. Referrer-Policy and Permissions-Policy are additional posture knobs — name them in notes when you set them on YOUR app. Wrong move: copying HSTS onto a broken HTTP-only lab and wondering why nothing happens.
Command guide
A02 Misconfig — HSTS and nosniff
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8769"
Command — copy this
curl -sS -m 3 -I "$SAFE/" | grep -i -E "strict-transport-security|x-content-type-options|x-frame-options" curl -sS -m 3 -I "$SAFE/hard" | grep -i -E "strict-transport-security|x-content-type-options|nosniff" curl -sS -m 3 -I "$SAFE/broken" | grep -i -E "strict-transport-security|x-content-type-options"
4. How you prove safely: identify DEMO, then curl -I on SAFE
168.0.1/ — Run a tiny HTTP server on 127.0.0.1:8769 that sets CSP, nosniff, and a frame header. curl -I and write the lines into notes. That is A02 header literacy. Do not curl random banks to “collect header datasets.” Do not hydra the home gateway.
5. What you record
Date (UTC). Asset http://192.168.0.1/ or SAFE 127.0.0.1:8769. Headers named: CSP, HSTS (concept), nosniff, frame control. Observed lines from YOUR curl -I. Ethics: NEVER BeEF; NEVER cookie-steal kits; NEVER hydra/nmap the LAN. Path: $HOME/cyberlium-lab/a02-headers-notes.txt, chmod 600. Legal: original Cyberlium — not official OWASP certification.
6. Wrong vs right: exploit kits vs headers on apps you run
Worked failure — same header names, opposite job. Right never needs BeEF.
Wrong
Ship BeEF/XSS payload packs to “test CSP.” Hydra 192.168.0.1. Probe stranger sites. Call missing headers on a bank your finding without RoE.
Right
Identify DEMO; set CSP/nosniff/frame headers on HOST 127.0.0.1:8769; record curl -I; lock a02-headers-notes.txt chmod 600. Next: Clickjacking and Frame Abuse.
Start the header demo, curl identify then SAFE, stop, lock notes.
Command guide
a02_headers_demo.sh — DEMO identify + CSP/nosniff on 127.0.0.1:8769
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl sudo apt install nmap sudo apt install python3
macOS:
Command — copy this
brew install nmap brew install python3
Windows: Built-in (PowerShell: Invoke-WebRequest)
Command — copy this
choco install nmap # or download https://nmap.org/download.html
Download https://python.org/downloads/
═══ COMMANDS ═══
Command — copy this
cd "$HOME/cyberlium-lab"
Command — copy this
SAFE="http://127.0.0.1:8769"
Command — copy this
cat > a02_headers_demo.py << 'PY'
from http.server import BaseHTTPRequestHandler, HTTPServer
HOST = "127.0.0.1" # bind only — never 0.0.0.0, never argv
PORT = 8769
if HOST != "127.0.0.1":
raise SystemExit("refusing non-loopback bind")
class H(BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
print("[lab]", fmt % args)
def do_GET(self):
body = b"<html><body>cyberlium a02 headers demo — DEMO writeup http://192.168.0.1/</body></html>"
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Security-Policy", "default-src 'self'; script-src 'self'")
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("X-Frame-Options", "DENY")
self.send_header("Referrer-Policy", "no-referrer")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
print("bind", HOST, PORT)
print("demo_writeup_url http://192.168.0.1/")
print("if 192.168.0.1 is a router, curl -I", f"http://{HOST}:{PORT}/")
print("ethics: NEVER BeEF / cookie-steal kits; NEVER hydra/nmap the LAN")
print("legal: original Cyberlium — not official OWASP certification")
HTTPServer((HOST, PORT), H).serve_forever()
PYCommand — copy this
cat > a02_headers_practical.sh << 'SH' SAFE="http://127.0.0.1:8769" curl -sS -I "$SAFE/" | grep -i -E 'HTTP/|Content-Security-Policy|X-Content-Type-Options|X-Frame-Options|Referrer-Policy' SH
Command — copy this
{Mission: a02-headers-notes.txt in cyberlium-lab (mode 600)
1) 0.0.1:8769.2) curl -I SAFE; paste CSP/nosniff/frame lines into notes; chmod 600. 3) Ethics: no BeEF/kits, no hydra/nmap of the LAN.
Stuck? Ask Cyberlium AI Mentor
If “CSP only matters if I pop XSS” still feels true, ask for a hint — not a payload. Try: "Hint only: what CSP and nosniff do, which curl -I lines to copy from 127.0.0.1:8769, and where locked a02-headers-notes.txt lives?"
You now treat missing security headers as A02 posture gaps — CSP, nosniff, frame controls, HSTS literacy — demonstrated with curl on YOUR lab or SAFE loopback with locked notes. Original Cyberlium — not official OWASP certification. Next — Clickjacking and Frame Abuse.
Knowledge Check
APPLY: Your lab response lacks CSP and nosniff. What A02 issue is that, and what do you do?
Multiple choice
Knowledge Check
APPLY: True or False: A CSP with unsafe-inline everywhere is as strong as a tight script-src 'self' policy.
True or False
Knowledge Check
APPLY: curl http://192.168.0.1/ is a Netgear router login. What do you do for the headers lab?
Multiple choice