CSE 134B Web Client Languages
  1. Foundations
  2. Qualities
  3. Security

Quality

Security

The browser runs code it got from strangers, on a machine that belongs to someone else. Security on the client is mostly about what you choose to let in, and about knowing which protections were never yours to give.

What it means for a web client

Start with the trust boundary. The client is untrusted: anyone can open DevTools and change any HTML, CSS or JavaScript you sent. The network is hostile unless HTTPS encrypts it. The server is the only place you control, so it is where validation, authorization and business logic must live. You validate on the client for usability and on the server for security. A maxlength, a readonly or an <input type="hidden"> is a convenience for honest users, not a lock.

Inside the browser, the unit of trust is the origin: scheme, host and port. Two pieces of content share trust if and only if they share an origin. The same-origin policy is precise about what it stops. A page may send a request to another origin and may embed another origin's images, frames and fonts, but it may not read the response or inspect what it embedded. CORS is the opt-in by which the owner of the data lets another origin read it.

The dangerous exception is the script. A <script src> from any server runs inside your origin with every power your own code has. It can read the DOM, read what the user types, read localStorage, and send all of it somewhere else. Cross-site scripting (XSS) is the same exception turned against you: an attacker's markup reaches the HTML parser, and the script it carries is trusted because it is in your origin, not because it is trustworthy. The same-origin policy guards the door between origins. It cannot help with code you carried inside.

So the defenses are layered, and each assumes the others may fail: TLS against eavesdropping and tampering in transit; the same-origin policy and CORS against cross-origin data theft; Content Security Policy and Subresource Integrity against injected or altered code; HttpOnly, Secure and SameSite cookies against stolen sessions and forged requests; the sandbox and Site Isolation underneath, containing a breach to one site. Most of these are things you declare in a header or an attribute, and the browser enforces what you declared.

Where it lives in each layer

Structure
URLs are user-controllable input. Every part of one can be edited, so a query parameter such as ?role=admin or ?user_id=1 proves nothing until the server checks authority. A user-supplied URL dropped into an href can carry the javascript: scheme; accept only http: and https:. A form that sends a password with GET puts it in the address bar, the history, the server logs and the Referer header. Every element that names another server, <script> above all, is a trust decision made in markup.
Presentation
A script that can draw on your page can draw over it, and a value dropped into a style attribute can load a URL or lay a transparent element across the page. Clickjacking is the same idea from outside: your real page, framed invisibly under someone else's bait. The answer is to say who may frame you, with the frame-ancestors directive of Content Security Policy.
Interaction
This is where XSS gets written. textContent writes characters and never invokes the HTML parser; innerHTML, outerHTML, insertAdjacentHTML and srcdoc all parse. Use textContent for every value that came from outside your code, and parse URLs before assigning them to href or src. A component that takes rich content through an attribute and renders it with innerHTML has the same hole; slots do not.
Delivery
A response body is input from outside your program, even from your own endpoint, because that endpoint stores what somebody typed. A blocked cross-origin read tells your code nothing beyond TypeError, and the request usually reached the server anyway. Credentialed requests, preflights and same-origin proxies each change what you are trusting. The headers that carry the defenses, from Content-Security-Policy to Set-Cookie flags, travel here too.

Where the course teaches it

Check your own page

  1. Search your JavaScript for innerHTML, insertAdjacentHTML and outerHTML. For each one, trace where the string came from. If any part came from a form, a URL or a response, switch to textContent or build the nodes yourself.
  2. List every <script src> that points at another server. For each, say why you trust that organization with your whole page, and add an integrity hash with crossorigin or remove it.
  3. Open DevTools, delete a required, raise a maxlength and edit a hidden field, then submit. Whatever reaches the server is what an attacker can send.
  4. Check every form that carries a password or personal data: it must use POST, and the page must be served over HTTPS.
  5. Open Application, then Cookies, and read the HttpOnly, Secure and SameSite columns for anything that holds a session.
  6. Look at your response headers in the Network panel. If there is no Content-Security-Policy, write down what script-src and connect-src would have to allow, and notice what that list tells you about your page.