See PHI399. Ref T4340. This header provides an additional layer of protection against various attacks, including XSS attacks which embed inline <script ...> or onhover="..." content into the document.
style-src: The "unsafe-inline" directive affects both style="..." and <style>. We use a lot of style="...", some very legitimately, so we can't realistically get away from this any time soon. We only use one <style> (for monospaced font preferences) but can't disable <style> without disabling style="...".
img-src: We use "data:" URIs to inline small images into CSS, and there's a significant performance benefit from doing this. There doesn't seem to be a way to allow "data" URIs in CSS without allowing them in the document itself.
script-src and frame-src: For a small number of flows (Recaptcha, Stripe) we embed external javascript, some of which embeds child elements (or additional resources) into the document. We now whitelist these narrowly on the respective pages.
This won't work with Quicksand, so I've blacklisted it for now.
connect-src: We need to include 'self' for AJAX to work, and any websocket URIs.
Clickjacking: We now have three layers of protection:
- X-Frame-Options: works in older browsers.
- frame-ancestors 'none': does the same thing.
- Explicit framebust in JX.Stratcom after initialization: works in ancient IE.
We could probably drop the explicit framebust but it wasn't difficult to retain.
script tags: We previously used an inline <script> tag to start Javelin. I've moved this to <data data-javelin-init ...> tags, which seems to work properly.
__DEV__: We previously used an inline <script> tag to set the __DEV__ mode flag. I tried using the "initialization" tags for this, but they fire too late. I moved it to <html data-developer-mode="1">, which seems OK everywhere.
CSP Scope: Only the CSP header on the original request appears to matter -- you can't refine the scope by emitting headers on CSS/JS. To reduce confusion, I disabled the headers on those response types. More headers could be disabled, although we're likely already deep in the land of diminishing returns.
Initialization: The initialization sequence has changed slightly. Previously, we waited for the <script> in bottom of the document to evaluate. Now, we go fishing for tags when domcontentready fires.