LCA: CSP for cross-site scripting protection
At linux.conf.au 2013 in Canberra, Mozilla's François Marier presented a talk on the Content Security Policy (CSP), the browser-maker's proposed approach to thwarting cross-site scripting attacks with a framework of granular restrictions on what types of content a page can load.
We covered CSP in July 2009, just a few months after development started. Since then, the idea has been expanded, and, in November 2012, version 1.0 was declared a Candidate Recommendation by the World Wide Web Consortium (W3C).
Cross-site scripting attacks, Marier explained, usually occur when input and variables in a page are not properly escaped. An unsanitized variable such as a user input field allows an attacker to inject JavaScript or other malicious code that is loaded by a visitor's browser. Even the templating systems used by modern content management systems (CMS)—many of which auto-escape content—are not foolproof. CSP offers an additional layer of protection, argued Marier, because it is implemented as an HTTP header to be delivered by the web server and not by the CMS. Thus, for an attacker to defeat a CSP-equipped site, he or she would have to compromise the web server, which is arguably more robust than the CMS.
A CSP policy is declarative, in which a site or web application specifies the locations from which it wishes to allow scripts and other page content to load. The header declares one or more src directives, each of which specifies a list of acceptable URIs for a specific content type. For example, the most basic policy
default-src 'self';
permits only loading content from the same site—in this case
meaning matching the protocol scheme, host, and port number. The
specification includes nine src directives:
default-src, script-src, object-src,
style-src, img-src, media-src,
frame-src, font-src, and connect-src. Each
directive can be set to none, or to a set of space-separated
expressions, optionally featuring the * wildcard. URI values are
matched according to a standard algorithm that looks for
scheme:host:port syntax. For example, the directive
img-src 'self' data ;
from a site at www.foo.org would match both www.foo.org and
data.foo.org. A site that uses external hosts for content
delivery or to serve ads would need to specify more complicated rules.
There is also a special reserved expression for allowing inline content (such as inline scripts or CSS), which is somewhat editorially named unsafe-inline as a reminder that permitting such inline content is a risky prospect. The reason this warrants the unsafe moniker being written into the specification itself, said Marier, is that a browser has no way to distinguish inline scripts that are written into the page at the original server from any scripts which are injected into the page content by an attacker.
The default-src directive allows
site owners to set a restrictive generic policy, which is then
overwritten only by whitelisting
specific additional content types, he said. At his personal site,
fmarier.org, he has the default-src directive set to
none and only turns on additional directives for "minor
stuff
".
Policy makers
At the moment, CSP is available and "works really
well
" in Firefox and Chromium/Chrome, and is somewhat
functional in Safari 6 or greater. Nevertheless, he continued, one
does not need to jump directly into converting one's sites over to
full CSP, which can be tricky to get right on the first try. He
instead suggested a few steps to implement CSP progressively.
The first step is removing all inline scripts and styles from the site's pages. Simply moving them to external files should not affect page functionality at all, and it removes the need to worry about unsafe-inline (although, it should be noted, external scripts and stylesheets do mean longer load times). The next step is to remove all <javascript:> URIs, which, of course, may entail some rewriting. Then one can proceed to implementing a CSP policy. Marier recommended starting with a "relaxed" and permissive policy, then working one's way progressively toward a stricter policy.
For this, CSP provides a helpful report-uri directive. Unlike the other directives, report-uri does not set policy; it tells the browser to report a policy violation to the URI provided as the value. The example Marier provided is:
report-uri http://example.com/report.cgi
which, he said, would allow one to log false-positive matches. It is
important to note, however, that when report-uri is in place,
CSP does not block the rule violations it catches, so it is
vital to remove it once testing is complete.
Marier also recommended that interested site administrators add their CSP rules in the web server, not through their CMS or application framework, specifically to provide the extra layer of protection described above. It is also useful as a reminder that CSP is a complement to standard cross-site scripting hygiene, and not a replacement for input escaping. There are some resources out there for site maintainers to get started with policy writing, he said, such as CSPisAwesome.com, a tool for generating valid policies.
For users who are keen to get the benefits of CSP but cannot wait for their sites to get it rolling, he recommended installing a browser extension that implements CSP on the client-side. There appears to be just one at the moment: UserCSP for Firefox. This extension allows users to write policies for the various sites they visit, which Firefox then applies just as it would a CSP header originating from the server. Obviously, the user needs to be aware of the risks of "injecting" (so to speak) CSP into their browser, since applying a user-crafted policy could break the site's functionality. On the other hand, by putting the policy decision in the user's hands, the user can find his or her own balance between what breaks and what risks are left open—as is the case with other client-side security extensions like NoScript.
HTTPS, almost everywhere
As a "bonus header," Marier also discussed the HTTP Strict Transport Security (HSTS) policy framework with the time remaining in his session. HSTS, like CSP, is an HTTP header mechanism. It is designed to protect again SSL downgrade attacks, in which an HTTPS connection is stripped down to HTTP, presumably without attracting the user's attention. HSTS allows the server to declare that it will only allow browsers to connect over HTTPS. The header does not fix a permanent condition; it includes a max-age directive giving a time in seconds for which the browser should cache the HSTS setting.
Firefox has supported HSTS since Firefox 4, but as a question from the audience revealed, it comes with one hangup: the browser must successfully connect to the server over HTTPS the first time in order to get the HSTS header. Mozilla sought to alleviate the risk of attacks that exploit this by shipping Firefox 17 pre-loaded with a list of verified banking web sites that the browser should access over HTTPS the first time.
HSTS is supported in Chromium/Chrome in addition to Firefox, as
well as in Opera. Mozilla cannot do much to implement security policy for other
browsers—particularly the proprietary ones—so when asked
what to tell users of other browsers, Marier's response was "It works
in these browsers. If it doesn't work in your favorite browser
... switch browsers.
"
That is probably sound advice, which a lot of free software
security mavens would echo. But it is interesting to see that,
with both CSP and HSTS, Mozilla is pushing forward on web security from
the server side as well as within the browser itself.
| Index entries for this article | |
|---|---|
| Security | Cross-site scripting (XSS) |
| Security | Web browsers |
| Conference | linux.conf.au/2013 |
