22 — Web Security — Defending the Browser's 'Run Anyone's Code' Model
CORS, CSP, TLS, OWASP, XSS, CSRF — a wall of acronyms that all somehow meant "bad thing, fix it." Writing them down collapsed the wall into one idea I could actually hold.
The model that clicked: *the browser's foundational decision is to run anyone's code and fetch anyone's resources, and it decides what to trust by origin. Every web security defense I've met is a response to that one decision. Each one either enforces the origin boundary (CORS), encrypts the pipe between origins (HTTPS/TLS), declares which sources are trusted to execute (CSP), or enumerates the known attacks* so you know what you're defending against (OWASP Top 10). Four jobs, one model. Once I saw it that way, the acronyms stopped being a list and started being a layered defense.
The premise worth stating plainly
A web page is unusual software. A native app is a binary I chose to install and the OS largely trusts; a web page is HTML, CSS, and JavaScript that the browser fetches and executes on every visit, from servers it didn't vet [1]. The browser can't refuse to run it — that's the whole point of the web. So instead it asks one question on which everything hangs: where did this come from? That "where" is the origin — the combination of scheme, host, and port (https://aveshina.my.id:443). Same origin is implicitly trusted; different origin is not. Every defense below is a refinement of that single rule.
CORS — enforcing the origin boundary
The browser's default rule is the same-origin policy: a script running on aveshina.my.id may not read responses from requests it makes to api.twitter.com [2]. That sounds restrictive, and it is —deliberately. Without it, any site I visited could silently call my bank's API using my cookies and read the response.
CORS (Cross-Origin Resource Sharing) is the relaxation mechanism, not the restriction. It's how a server at api.example.com can say to a browser, "responses from me are allowed to be read by scripts at app.example.com" [2][3]. It works entirely through HTTP headers. For simple requests the server returns:
Access-Control-Allow-Origin: https://app.example.comFor requests that could change data (anything with a custom header, or methods like PUT/DELETE, or non-simple content types), the browser sends a preflight — an OPTIONS request asking "is this allowed?" — before the real request [3]. The thing I had to straighten out: CORS is enforced by the browser, not the server. The server is just declaring policy via headers; the browser is the one that blocks the response from reaching the script if the headers don't permit it. The same API called from curl has no CORS gate at all — because curl doesn't implement the same-origin policy.
CORS also mitigates CSRF (Cross-Site Request Forgery) in practice: because cross-origin requests are gated, a malicious site can't trivially fire authenticated state-changing requests at my API and have them succeed — though CSRF ultimately needs its own defenses too (anti-CSRF tokens — one-time values a form must echo back — and SameSite cookies, which tell the browser not to send the cookie on cross-site requests), since simple form POSTs predate CORS and are allowed by default.
HTTPS — encrypting the pipe
Same-origin policy assumes I can actually tell who I'm talking to and that the bytes aren't being tampered with in flight. On plain HTTP that assumption is false — anyone between the browser and the server (the coffee-shop Wi-Fi, an ISP, a compromised router) can read and alter the traffic.
HTTPS is the same HTTP conversation from my earlier notes, wrapped in TLS — an encryption layer that gives two guarantees [4][5]:
- Confidentiality — the bytes on the wire are encrypted; the man in the middle sees scrambled data (ciphertext).
- Integrity — TLS authenticates the server (via its certificate) and detects any tampering with the data.
The browser's padlock icon means exactly: this conversation is encrypted and the server proved who it is. It does not mean "this site is safe" or "this site is honest" — a phishing site can have a valid certificate and a padlock too. The certificate proves identity, not intent [4]. The single practical move this left me with: never send anything I'd mind an attacker reading over plain HTTP, and treat a site that loads mixed HTTP subresources on an HTTPS page as broken — the browser will block or warn.
CSP — declaring trusted sources
CORS gates cross-origin reads. CSP (Content Security Policy) gates what's allowed to execute at all, even from the same origin. It's a defense against XSS (Cross-Site Scripting) — the attack where untrusted input ends up executed as JavaScript on my page because I didn't escape it [6][7].
CSP is a declarative allowlist, sent as a header (or <meta> tag), that names the trustworthy sources for each resource type. A strict policy looks like:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;default-src 'self' is the spine: unless a more specific directive says otherwise, every resource — scripts, styles, images, fonts, frames — may only come from the page's own origin [7]. No inline scripts, no eval (a function that runs a string as code), no third-party CDNs (content delivery networks) I didn't explicitly allow. The payoff is that even if an attacker does slip <script>alert(1)</script> into my HTML, the browser refuses to execute it, because inline scripts aren't in the allowlist. CSP turns XSS from "one missed escape and you're owned" into "one missed escape that the browser quietly refuses to run."
It also mitigates clickjacking (an attacker embedding my site in a hidden <iframe> and tricking the user into clicking it) via frame-ancestors 'none' — the modern replacement for X-Frame-Options [7]. And it can report violations to a URL with report-uri, so I see in production the moment something tries to load from an untrusted source.
OWASP Top 10 — naming the known attacks
CORS, HTTPS, and CSP are defenses. OWASP is the map of what's attacking. The OWASP Top 10 is a periodically-updated list of the ten most critical web application security risks, compiled from real incident data [8][9]. It's not exhaustive, and the exact ordering shifts between editions, but it's the shared vocabulary — when someone says "that's an A03," they mean injection, and now I know what they mean.
The entries I keep close, because they're the ones the frontend actually touches:
- Injection (SQL, command, etc.) — untrusted input reaches an interpreter (a program that runs text as code, like a database or shell). The frontend's job here is mostly to get out of the way: parameterized queries (queries with placeholders, so input can't be run as code) on the backend do the real work; client-side validation is UX, not security.
- XSS — covered above; it's injection where the interpreter is the browser. CSP is the browser-side backstop, escaping/output-encoding is the fix.
- Broken Access Control / CSRF — the server doing something on behalf of a user who didn't intend it. CORS helps, but the real fix is server-side authorization checks, anti-CSRF tokens, and SameSite cookies.
- Security Misconfiguration / Vulnerable Dependencies — default credentials, verbose errors, an npm install that pulled in a package with a known CVE (a published vulnerability ID). The last one is where I actually spend time: npm audit, keeping the lockfile current, and not trusting a package just because it has a lot of stars.
The value of OWASP for me isn't memorizing the list. It's that when I'm building a feature, I can run the Top 10 as a checklist of failure modes — "did I consider injection here? XSS? access control?" — instead of waiting for a bug report to tell me what I forgot.
How these compose
The four defenses don't compete; they sit at different points in a single request's life:
OWASP isn't a gate in that flow — it's the map I use while building it, telling me which gates to put where. HTTPS protects the wire. CORS protects the server from unauthorized cross-origin callers. CSP protects the browser from executing the wrong thing. Each closes a hole the others don't.
How I use this
The habit these notes left me with is a single question when I ship a feature: which of the four am I relying on here, and is it actually configured? A fetch to a new API — is CORS configured on the server, or am I about to discover it at deploy time? Anything handling user input — is it escaped on output, and is there a CSP header catching what I miss? Any page handling auth — is it HTTPS-only, and are the cookies SameSite and Secure? Running the OWASP Top 10 as a mental checklist turns "is this secure?" from a vague dread into a short, finite list of things to verify. The model — origin trust, with four layered defenses — is what made that list finite.
References
[1] OWASP, "OWASP Top Ten," 2023. [Online]. Available: https://owasp.org/www-project-top-ten/
[2] Mozilla, "Cross-Origin Resource Sharing (CORS)," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
[3] R. Bika, "Understanding CORS," rbika.com, 2020. [Online]. Available: https://rbika.com/blog/understanding-cors
[4] Cloudflare, "What is HTTPS?," 2024. [Online]. Available: https://www.cloudflare.com/en-gb/learning/ssl/what-is-https/
[5] Google, "Why HTTPS Matters," web fundamentals, 2022. [Online]. Available: https://developers.google.com/web/fundamentals/security/encrypt-in-transit/why-https
[6] OWASP, "OWASP Cheat Sheet Series," 2024. [Online]. Available: https://cheatsheetseries.owasp.org/
[7] Mozilla, "Content Security Policy (CSP)," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
[8] Google, "Content Security Policy (CSP)," web.dev, 2023. [Online]. Available: https://web.dev/articles/csp
[9] The New Stack, "OWASP Top 10: A Guide to the Worst Software Vulnerabilities," 2023. [Online]. Available: https://thenewstack.io/owasp-top-10-a-guide-to-the-worst-software-vulnerabilities/
Knowledge check · Question 1 of 5
A script on aveshina.my.id fetches data from api.twitter.com and the browser blocks reading the response. Who actually enforced that block?
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!