How we made reading letters safer: Content Security Policy in Yandex.Mail
One of the priorities for the Yandex.Mail team has always been and is the security of user data. And this concerns not only the storage of letters, but also secure access to them. Back in 2011, we began to pass all images in letters through our proxy servers, blocking one of the distribution channels of malicious code, and also cache them to save traffic and ensure greater privacy. In November of this year, we they implemented encryption when receiving and sending mail, and also transferred mail to HTTPS-only mode - now the web interface is available only via a secure protocol.
And more recently, we have begun to support a new user data protection mechanism - the Content Security Policy standard. With it, you can prevent scripts on the page from loading any resources from hosts that are not listed in the white list.
This is still a rather rare thing (not a single large post known to us yet uses this), and in this post we will share our experience in implementing the standard. ')
XSS , despite all the knowledge, is one of the most common vulnerabilities of sites. This vulnerability allows an attacker to insert malicious code into a web application page. How to control it on the server, we know very well. But on the user side, this is all somewhat more complicated. What methods of protection do we know?
User Input Validation and Web Application Firewall (WAF);
special character escaping;
protecting cookies using HttpOnly so that they cannot be read from JavaScript;
various browser plugins, for example, noscript.
Now there is another protection mechanism against such attacks - Content Security Policy.
Content Security Policy ( CSP ) is a new standard that defines Content-Security-Policy and Content-Security-Policy-Report-Only HTTP headers that tell the browser a white list of hosts from which it can download various resources. The current status of the standard is Candidate Recommendation.
Today, it is supported by all popular browsers:
Chrome 25+, Firefox 23+, Opera 15+, and Yandex Browser have full support and understand the standard header;
Firefox 4-22, IE 10+ support non-standard X-Content-Security-Policy header and have partial support for the standard one;
Chrome 14-24, Safari 5-7 support the non-standard X-Webkit-CSP header and have partial support for the standard one.
For information, you can read on HTML5 Rocks article by Mike West (Mike West) - one of the developers of the standard. A good standard description is on MDN .
What does CSP consist of?
The CSP header consists of a set of divided directives - parts of a policy that control certain browser resources.
In the current version of the standard, the following set of directives is available:
default-src specifies a list of hosts that are assigned by default to unspecified directives.
script-src, style-src, object-src (for plugins like Flash), img-src, media-src (audio and video), frame-src (iframe), font-src, connect-src (XMLHttpRequest, WebSocket, EventSource) - narrower directives that control the corresponding browser resources. For each directive, you must specify a list of hosts (not urls) with which the browser can communicate. Can be used *.
report-uri - specifies the URL to which the JSON violation reports will be sent. This is how it looks like:
Some browsers also report a link and a JS line in the report, which led to a breach of security policy.
In addition, in the directives you can use not the hosts, but keywords (always in quotes):
'self' - corresponds to the current host, protocol and port.
'none' - everything is forbidden.
'unsafe-inline' is used in script-src and enables , javascript: - (onclick=""). style-src style="". , .. javascript , XSS. 'unsafe-eval' β script-src : eval, new Function, setTimeout(' var foo = "bar" ', 1). , javascript: - (onclick=""). style-src style="". , .. javascript , XSS. 'unsafe-eval' β script-src : eval, new Function, setTimeout(' var foo = "bar" ', 1).
Hosts can be specified as simply "yandex.st" , and with the protocol or port "https://yandex.st:443" . Remember that if the host does not have a protocol or port, then it is taken from the current page, by analogy with the same origin policy. Thus, the host "yandex.st" on the page "https://mail.yandex.ru:443/neo2/" automatically takes the form "https://yandex.st:443" .
If the Content-Security-Policy header is specified, the browser blocks all resources that do not comply with the policy. The Content-Security-Policy-Report-Only header also checks all resources, but does not block them, but only reports violations. We recommend using it in the early stages of CSP implementation.
How we implemented
We investigated this technology from the spring, when there was no standard implementation yet. First, carefully implemented the Content-Security-Policy-Report-Only header for our internal mail. Some time we studied the reports and corrected our policy, and in May we included CSP in blocking mode. During internal testing, the behavior of all headers, both standard and none, was investigated.
With public mail it was somewhat more difficult, because users came to us with an uncontrolled environment. For several months, we understood the reports, corrected the politicians, and finally, in the fall, rolled them out 100% in blocking mode.
The most interesting thing that we now have is several million daily reports with violations from viruses, careless plug-ins and extensions that try to insert their code into our pages. We cannot control the content of this code, which means we cannot guarantee that it treats the personal data of our users with care - and we are pleased that we block their execution.
In addition, during the implementation process, we developed tools for working with CSP:
Chromium-based browser extension to add headers to the page and test policies.
A utility for parsing browser reports to make it easier to get a list of blocked resources.
Let us analyze the title on the example of mobile Yandex.Mail
A rough plan for implementing CSP on a service is as follows:
Evaluation of the list of downloadable resources.
Implementing the Content-Security-Policy-Report-Only header.
Log analysis.
Policy correction.
Implementing the Content-Security-Policy header (switching to blocking mode).
The happiness of users :)
What to consider when implementing
Browser features:
Safari 5 and AndroidBrowser with the X-Webkit-CSP header have a very poor implementation of the standard. And we advise you not to use CSP for these browsers at all. For example, they poorly understand the unsafe-eval and unsafe-inline rules.
Firefox in X-Content-Security-Policy implements slightly non-standard directives. Instead of connect-src, you need to write xhr-src (or you can add rules to default-src). In addition, he does not understand unsafe-inline, unsafe-eval, instead of them it is necessary to add the directive "options inline-script eval-script". You can read more about your own implementation of the header on the Mozilla wiki .
Firefox and X-Content-Security-Policy have problems with report-ui.
Safari in iOS6 sends very uninformative reports.
We do not recommend using custom headers. The latest versions of modern browsers (with the exception of IE) support the standard header, therefore, using only it, you cover the vast majority of the audience.
Features CSP, which must always be remembered:
If you use inline-pictures, then in img-src you need to enable the "data:" protocol.
Using Blob, specify 'self' (for Chrome) and blob: (for Firefox and X-WebKit-CSP)
The CSP of the top document does not apply to child iframes except for about: blank.
If you do not want to block chrome extensions, which, by the way, from the latest versions also live on CSP, then you need to allow the βchrome-extension:β protocol
default-src applies to all unspecified directives, but if you want to add or remove something, you will have to specify all domains again.
You can specify both Content-Security-Policy and Content-Security-Policy-Report-Only. Both headers will work independently. This approach will be useful for testing new policies.
If you use frameworks with feature detection (for example, jQuery <1.8 or Modernizr), then for style-src you must specify 'unsafe-inline'.
Naturally, this is not always possible, but try not to use *, but specify the exact list of domains. Also specify all directives, otherwise all errors will occur as a violation in default-src. Of course, the size of the header will increase, but it will be much easier to find problems.
Unfortunately, we have not yet managed to refuse unsafe-inline. Inline scripts in the mail are used for two things: issuing settings and checking the loading of critical JS (jQuery and bootloader). And if we could change the settings to JSON, then we could not refuse important reports of JS unloading, and we had to leave 'unsafe-inline'. Also, problems were added by the active use of inline attributes in the TinyMCE WYSWYG editor.
Although this rule allows you to execute any inline javascript on the page, we can protect ourselves by allowing connections only to trusted resources.
Updated CSP 1.1 standard
The new version of the standard is currently in the draft stage, but Chrome and Firefox are already beginning to introduce new features from it.
For example, the new nonce directive may be a solution to inconvenience with a complete ban on unsafe-inline. The work scheme is still under development, but we are making efforts so that it works as follows:
nonce - a random sequence of characters transmitted in the header;
if there is unsafe-inline and nonce, then nonce turns off unsafe-inline;
only inline scripts are executed that are signed by the nonce attribute with the same sequence of characters.
CSP was not only protection against attacks and blocking incomprehensible requests. In testing, we were pleasantly surprised by two things:
In essence, CSP allows you to make a distributed security testing system out of users' computers. And if the number of reports of violations increases dramatically, this is a reason to go looking for a problem.
Since all mail users are working on HTTPS, CSP made it possible to track and fix those secluded places where requests were still sent via HTTP.
Try CSP, embed, protect users' personal data and just make the world a better place.