… we just don’t know it yet.

I first encountered the Content Security Policy HTTP header earlier this year when one of our users reported the Instapaper bookmarklet wasn’t working on GitHub. Triggering the bookmarklet, and inspecting element on the page revealed the problem…

Refused to load the script ‘https://www.instapaper.com/j/redacted?u=https%3A%2F%2Fgithub.com%2Fcocoapods%2Fcocoapods&t=1414077850205' because it violates the following Content Security Policy directive: “script-src assets-cdn.github.com collector-cdn.github.com”.

A cursory glance at the Content Security Policy specification revealed the problems. Proposed as a solution to a suite of cross-site scripting (XSS) vulnerabilities, Content Security Policy allows web authors to specify the origin hosts of web content, this includes:

  • JavaScript (script-src)
  • AJAX Calls (connect-src)
  • CSS (style-src)
  • Fonts (font-src)
  • Frames (frame-src)
  • Images (image-src)
  • Video & Audio (media-src)
  • Flash (object-src)

The crux of the issue for bookmarklets is that web authors can control the origin of the JavaScript, network calls, and CSS, all of which are necessary for any non-trivial bookmarklet. The W3C specification for Content Security Policy 1.0 has a specific clause to protect end-users from this type of authoritarian control:

Enforcing a CSP policy should not interfere with the operation of user-supplied scripts such as third-party user-agent add-ons and JavaScript bookmarklets.
W3C Content Security Policy 1.0, Section 3.3 November 15th, 2012

The above clause treats browser extensions and bookmarklets as trusted components of the browser ecosystem, and intentionally prevents web authors from blocking the functionality provided by components specifically added and initiated by an end-user.

Some have contended that bookmarklets and add-ons can not be trusted and can be compromised by privilege escalation or other security vulnerabilities unrelated to cross-site scripting. These hypothetical, compromised add-ons could effectively negate any benefit of enforcing a Content Security Policy. As a result, the working draft of Content Security Policy 1.1 has been altered to:

Note: User agents may allow users to modify or bypass policy enforcement through user preferences, bookmarklets, third-party additions to the user agent, and other such mechanisms.
W3C Content Security Policy 1.1 (Working Draft) Section 5
July 3rd, 2014

The altered language significantly weakened the stance that browsers should bypass Content Security Policy for user-supplied add-ons, and suggests that providing the capability for users or add-ons to modify the Content Security Policy is optional. And it is.

Content Security Policy is currently being enforced by all major browsers, and is used by major websites like GitHub, Twitter, and Medium. Support for modifying Content Security Policy is non-existent for users, tenuous for browser extensions, and impossible for bookmarklets. The end result is, unfortunately, an all too familiar story: we’ve sacrificed end-user freedom for the promise of additional security.

One of the HTML5 Design Principles, Priority of Constituencies, defines the course of action to take in the event of a conflict between stakeholders:

In case of conflict, consider users over authors over implementors over specifiers over theoretical purity. In other words costs or difficulties to the user should be given more weight than costs to authors; which in turn should be given more weight than costs to implementors; which should be given more weight than costs to authors of the spec itself, which should be given more weight than those proposing changes for theoretical reasons alone. Of course, it is preferred to make things better for multiple constituencies at once.
HTML5 Design Principles Section 3.2

The ultimate catch-22 of the new Content Security Policy wording is that it’s intended to benefit the users, by providing additional security from hypothetical malicious add-ons on websites that enforce a Content Security Policy. In the end the bookmarklet has been relegated obsolete by the change, a casualty of one clause in one section of one web specification, and end-users and developers are the ones who will mourn its demise. The path to hell is paved with good intentions.

So what can we do? Well the deadline for comments on the working draft of Content Security Policy 1.1 was August 14th, 2014. Major browsers are enforcing Content Security Policy with varying strictness, the trend is that more web authors will start enforcing Content Security Policy, and there’s no user-based recourse for changing it.

I’d probably try to do more about it, but I’m too busy rewriting Instapaper’s bookmarklet into extensions for every major browser.

RIP Bookmarklet (1995—2014)

Thanks to Rodion, Frank, Paul, Mike, and Kevin for reading drafts of this post.

Sources & Further Reading:

  1. An Introduction to Content Security Policy
  2. CSP to the Rescue: Leveraging the Browser for Security
  3. Content Security Policy 1.0
  4. Content Security Policy 1.1
  5. Subverting CSP policies for browser add-ons (extensions)
  6. CSP 1.1: Remove note about extensions.
  7. CSP 1.1: Add non-normative language for extensions
  8. [CSP] Request to amend bookmarklet/extensions sentence in CSP1.1
  9. Removal of the note about extensions
  10. CSP Formal Objection
  11. The Priority of Constituencies
  12. HTML5 Design Principles