Summary

  • The W3C's first public Content Security Policy draft of 29 November 2011 proposed a declarative list of permitted resource sources, with the browser enforcing the site's instructions.
  • The 15 December 2016 Level 2 Recommendation described enforced and report-only policies, resource directives, and nonces or hashes for selected inline scripts. The site wrote the guest list; the browser checked the guests.

The website had thrown a party and lost the invitations. One script came from a familiar address. Another came from an old widget nobody remembered installing. Images arrived with fonts, frames and connections to more distant services. Then an unwelcome script slipped into the crowd and tried to act as if it belonged there. The host could ask the browser to mind the door, but first it had to produce a guest list that matched the party it was actually running.

The W3C's first public Content Security Policy Working Draft of 29 November 2011 put that bargain into a browser-readable form. Its abstract described a declarative policy that let a web application restrict where it loaded resources, intended to mitigate content injection such as cross-site scripting. Instead of trusting every address that appeared in page markup or script, a site could send a rule describing acceptable sources. The browser would compare a requested resource with that rule before loading it.

The early list was more specific than a single “trusted” stamp. The draft described default-src for a baseline and directives for scripts, images, styles, fonts, frames, media and connections. A publisher could decide that images came from one set of places and scripts from a tighter set. That mattered because a harmless photograph and an executable script do different work once admitted. A theatre can sell popcorn at a side entrance without handing the popcorn seller the keys to the lighting booth.

This changed the division of labor. The site knew which suppliers it intended to use; the browser saw the actual request at the moment it happened. The policy joined those two views. The site could write script-src to narrow where scripts came from, while the browser enforced the instruction in the page's loading path. A policy was therefore only as thoughtful as the site's inventory of its own dependencies. If the guest list forgot the payment form, a legitimate checkout could meet the same closed door as an unwanted script.

By the W3C's Content Security Policy Level 2 Recommendation of 15 December 2016, the door had gained a rehearsal mode. A Content-Security-Policy-Report-Only response header told the browser to monitor policy violations while allowing the resource to load. The document's example used that mode to develop a policy iteratively before changing to an enforced Content-Security-Policy header. The bouncer could spend a shift taking notes before being told to turn anyone away.

That was an operationally shrewd addition. A large site rarely knew every script and image source by heart. Reports could reveal a forgotten dependency or a page whose requested resources differed from the publisher's mental map. They were observations from the browser, not a magic label saying which request was an attack. The operator still had to decide whether a reported source belonged in the product and whether to change the page or the policy. The report-only desk made the site's own sprawl visible.

The 2016 Recommendation preferred an HTTP response header for policy delivery, while also allowing a policy in an HTML meta element. The timing of that placement mattered. A meta policy did not reach back to content loaded before it appeared, and it could not carry report-only behavior. Putting a notice on the wall after the guests passed through the entrance makes for weak door work. The response header met the browser at the start of the document's journey.

Scripts posed the hardest invitation problem. Some legitimate pages carried inline code, even as broad permission for inline code offered an easy route for injected code. Level 2 described nonces and hashes as narrower ways to identify intended inline scripts. A fresh random nonce paired a policy with selected script elements; a hash identified exact script content. The site could say which particular scripts got a pass without declaring every scrap of inline code an honored guest.

The list also reached beyond scripts. The 2016 text described directives such as img-src, connect-src, base-uri, form-action and frame-ancestors. They addressed different kinds of activity: fetching an image, making a network connection, changing the base address used by relative links, submitting a form, or deciding which page could frame the site. The point was to place decisions where the site could state its intent and the browser could observe the request. One bouncer was checking several doors.

The W3C's Level 2 publication history records the path through drafts to the December 2016 Recommendation. That date is a specification milestone. The interesting historical shift is in the contract the documents developed: a publisher writes a resource policy, the browser evaluates loads, and reports give the publisher a chance to inspect the mismatch between its plan and its pages. The browser became a better doorman, but the website still had to know who it had invited.

A reader does not inspect a site's script-src header before opening an article or paying a bill. They see the result when the page loads correctly and the wandering script does not. The guest list is backstage work. Its first draft looked like a set of source directives; its durable lesson was more embarrassing and more useful. The web could now ask the browser to check the door, and a surprising number of sites had to learn the names of their own guests.