Part 10 — Web and HTTP security
The application is where most breaches actually happen. A perfect TLS configuration does nothing against SQL injection, a broken authorization check, or a poisoned dependency. This Part is the attacker's-eye view of a web app, mapped to the OWASP Top 10, with the fix for each.
Chapter 41 — How the web decides what to trust
In one sentence
Browser security rests on the origin (scheme + host + port) and the same-origin policy that isolates one site's data from another's — and almost every web vulnerability is a way to cross that boundary or abuse the server's trust in a request.
The origin
ORIGIN = scheme :// host : port
https://shop.securesh.op:443
\___/ \____________/ \_/
scheme host port
Same origin ONLY if all three match exactly:
https://shop.securesh.op vs http://shop.securesh.op -> DIFFERENT (scheme)
https://shop.securesh.op vs https://api.securesh.op -> DIFFERENT (host)
https://shop.securesh.op vs https://shop.securesh.op:8443 -> DIFFERENT (port)
https://shop.securesh.op/a vs https://shop.securesh.op/b -> SAME (path doesn't count)
"Site" is looser than "origin" — it's roughly the registrable domain
(securesh.op), ignoring scheme, port, and subdomain. Cookies and some newer
protections work at "site" granularity (eTLD+1). Know which one a given feature
uses.
Same-Origin Policy (SOP)
The foundational rule: script running on origin A cannot read data from origin B.
A page on https://evil.example CAN:
- send requests to https://bank.example (the request goes, with cookies!)
- load bank.example images, scripts, styles, iframes
- navigate to it, submit forms to it
A page on https://evil.example CANNOT:
- READ the response body from https://bank.example (SOP blocks it)
- read bank.example's cookies, localStorage, or DOM (in an iframe)
That gap — "the request is sent with your cookies, but the attacker can't read the reply" — is exactly why CSRF exists (the attacker doesn't need to read the reply, just to cause the side effect) and why CORS was invented (to let servers opt in to sharing responses).
Cookies — the ambient credential
Cookies are attached to every matching request automatically. That convenience is the root of a lot of pain. The attributes that matter:
Set-Cookie: session=abc123;
Secure; <- only sent over HTTPS
HttpOnly; <- JavaScript cannot read it (blunts XSS theft)
SameSite=Lax; <- not sent on most cross-site requests
(Lax: sent on top-level GET navigation;
Strict: never cross-site;
None: always -- REQUIRES Secure)
Path=/; Domain=securesh.op;
Max-Age=3600;
__Host- prefix <- name prefix that ENFORCES Secure + Path=/
+ no Domain -> hardest to tamper with
Defaults to set on every session cookie: Secure; HttpOnly; SameSite=Lax
(or Strict for high-value), and use the __Host- name prefix.
CORS — opting in to cross-origin reads
CORS is not a restriction you configure to be safe; it's the server telling the browser "it's OK to let this other origin read my response".
Browser (from https://app.example) --> GET https://api.example/data
Origin: https://app.example
api.example responds:
Access-Control-Allow-Origin: https://app.example <- echoes ONE origin
Access-Control-Allow-Credentials: true <- allow cookies/auth
Vary: Origin
The browser then lets app.example's JS read the body. Without these
headers, the request still HAPPENED, but JS gets an opaque error.
The classic CORS mistakes:
Access-Control-Allow-Origin: * WITH Allow-Credentials: true
-> forbidden by spec, but misconfigs/reflectors bypass it. Means
"any site can make authenticated calls and read the response."
Reflecting the Origin header without an allowlist -> same thing.
Allowing null origin -> sandboxed iframes / redirects can hit you.
Treating CORS as an auth mechanism -> it isn't. Non-browser clients
ignore it entirely. CORS protects the USER's browser, not your API.
The OWASP Top 10 (2025) — the map for this Part
A01 Broken Access Control -> Ch 45 (still #1, by far)
A02 Security Misconfiguration -> Ch 41,47 (headers, defaults, CORS)
A03 Software Supply Chain Failures -> Ch 47
A04 Cryptographic Failures -> Parts 3-7 (weak/missing crypto,
secrets in code, bad TLS)
A05 Injection (incl. XSS) -> Ch 42, 43
A06 Insecure Design -> whole guide (threat model first)
A07 Authentication Failures -> Ch 46
A08 Software & Data Integrity Failures -> Ch 47 (unsigned updates,
insecure deserialization, CI/CD)
A09 Logging & Alerting Failures -> Part 11
A10 Mishandling Exceptional Conditions -> Ch 44/45 (fail-open, verbose
errors, bad error handling)
(SSRF -> folded into A01/its own writeup) -> Ch 44
Rankings shuffle between editions; the underlying bug classes are stable. Learn the classes.
Practice (25 min)
# 1. Inspect real cookie hygiene
curl -sI https://github.com | grep -i set-cookie
curl -sI https://news.ycombinator.com | grep -i set-cookie
# which have Secure / HttpOnly / SameSite? which don't?
# 2. Probe a CORS policy (safe, read-only)
curl -s -I -H 'Origin: https://evil.example' https://api.github.com/ | grep -i 'access-control'
curl -s -I -H 'Origin: https://evil.example' \
-X OPTIONS https://api.github.com/ | grep -i 'access-control'
# does it reflect your Origin? echo *? require credentials?
# 3. See SOP in your browser console (devtools on any page):
# fetch('https://example.com').then(r=>r.text()).then(console.log)
# -> blocked by CORS (you'll see the error; the request still went)
# 4. Security headers scan
curl -sI https://your-service.example | grep -iE \
'content-security-policy|strict-transport|x-content-type|x-frame|referrer-policy|permissions-policy'
# or use: https://securityheaders.com / https://observatory.mozilla.org
Common confusions
- "CORS makes my API secure." CORS relaxes SOP for browsers. It is not authentication or authorization, and non-browser clients ignore it. Your API still needs real authz on every endpoint.
- "SameSite=Lax stops all CSRF." It stops most cross-site POST/AJAX CSRF, but
top-level
GETnavigations still send the cookie, and same-site subdomains can be a gap. Keep anti-CSRF tokens for state-changing requests (Ch 44). - "HttpOnly means XSS can't hurt me." It stops cookie theft via JS. An XSS can still make authenticated requests as the user, read the page, keylog, etc.
- "Origin and site are the same." Origin = scheme+host+port (exact). Site = eTLD+1 (looser). Cookies use "site"; SOP uses "origin".
Check yourself
- What three components make up an origin? Are
http://xandhttps://xthe same origin? - Under SOP, what can
evil.exampledo tobank.example, and what can't it? - What do
Secure,HttpOnly, andSameSiteeach protect against? - Is
Access-Control-Allow-Origin: *with credentials safe? What's the usual underlying mistake? - Does CORS protect your API from a Python script? Why/why not?
(Answers: Appendix F.)
Further reading
- Site: MDN Web Docs — "Same-origin policy", "CORS", "Set-Cookie", "Content Security Policy". The reference.
- Book: The Tangled Web, Michal Zalewski — how browsers actually behave. Older but the model is unchanged and it's brilliant.
- Site:
web.dev/articles/same-site-same-origin— the origin-vs-site explainer with diagrams. - Course: PortSwigger Web Security Academy — "CORS" topic, with free labs.
- Tool:
securityheaders.com, Mozilla Observatory.