Comment: Preface: After HTML5 appears, network security has attracted more widespread attention. What improvements has the Web made to network security? How do we face increasingly dangerous cyber fraud and attacks? The following article talks about W3C's latest solution to this problem. If I have the opportunity in the future, I will conduct HTML5 content on XSS, P3P, homologous policy, CORS (cross-domain resource sharing) and CSP.
The security policy of the World Wide Web is rooted in a homologous policy. For example, code can only access data, but does not have access permissions. Each source is separated from the rest of the network, creating a secure sandbox for developers. This is perfect in theory, but now the attackers have found a clever way to destroy the system.This is XSS cross-site scripting attack, bypassing homologous policies through fake content and tricking clicks. This is a big problem, if the attacker successfully injects the code, a considerable amount of user data will be leaked.
Now we introduce a brand new and effective security defense strategy to mitigate this risk, which is ContentSecurity Policy (CSP).
Source whitelist
The core of XSS attacks is that the browser cannot tell whether the script is injected by a third party or is really part of your application. For example, the Google +1 button will load and execute code from https://apis.google.com/js/plusone.js, but we cannot expect to tell from the pictures on the browser whether the code is really from apis.google.com or from apis.evil.example.com. The browser downloads and executes a page request for any code, regardless of its source.
CSP defines Content-Security-PolicyHTTP header to allow you to create a whitelist of trusted sources, so that the browser only executes and renders resources from those sources, rather than blindly trusting everything provided by the server. Even if an attacker can find a vulnerability to inject a script, it will not be executed because the source is not included in the whitelist.
The Google +1 button above is an example, because we believe that apis.google.com provides valid code, as well as ourselves, we can define a policy that allows the browser to execute scripts from one of the two sources below.
Content-Security-Policy:script-src 'self' https://apis.google.com
Isn't it very simple? script-src can control script-related permissions for specified pages. This way the browser will only download and execute scripts from and from this page itself.
Once we define this policy, the browser will throw an error when it detects the injected code (note what browser it is).
Content security policies apply to all common resources
Although script resources are the most obvious security risks, CSP also provides a rich set of instructions that allow pages to control the loading of various types of resources, such as the following types:
content-src: Restrict the type of connection (e.g. XHR, WebSockets, and EventSource)
font-src: Controls the source of network fonts. For example, you can use Google's web fonts through font-src https://themes.googleusercontent.com.
frame-src: Lists the source of frames that can be embedded. For example, frame-src https://youtube.com only allows videos embedded in YouTube. .
img-src: defines the source of the loadable image.
media-src: Restrict the source of video and audio.
object-src: Restrict the source of Flash and other plugins.
style-src: Similar to Script-src, it only works on the css file.
By default, all settings are on without any restrictions. You can separate multiple instructions by semicolons, but similar to script-src https://host1.com;script-src https://host2.com, the second instruction will be ignored. The correct way to write it is script-src https://host1.com https://host2.com.
For example, you have an application that needs to load all resources from a content distribution network (CDN, for example https://cdn.example.net) and know that the content of no frames and plugins is needed, your strategy might look like this:
Content-Security-Policy:default-src https://cdn.example.net; frame-src 'none'; object-src 'none'
detail
The HTTP header I used in my example is Content-Security-Policy, but modern browsers have already provided support through prefixes: Firefox uses x-Content-Security-Policy, WebKit uses X-WebKit-CSP. In the future, it will gradually transition to unified standards.
The strategy can be set for each different page, which provides a lot of flexibility. Because some pages on your site may have Google +1 buttons, while others do not.
The source list of each instruction can be quite flexible. You can specify a pattern (data:, https:), or specify a host name in a range (example.com, which matches any source, any mode and any port on the host), or specify a complete URI (https://example.com:443, specifically the https protocol, example.com domain name, port 443).
You can also use four keywords in the source list:
none: You may expect to mismatch anything
self: Same as the current source, but does not contain subdomains
unsafe-inline: Allows inline Javascript and CSS
unsafe-eval: Mechanisms that allow text to JS, such as eval
Please note that these keywords need to be quoted.
Sandbox
There is another instruction worth discussing here: sandbox. It is somewhat inconsistent with other instructions. It mainly controls the behavior taken on the page, rather than the resources that the page can load. If this property is set, the page will behave like a frame with the sandbox property set. This has a wide range of impact on the page, such as preventing form submissions, etc. This is a little beyond the scope of this article, but you can find more information in the HTML5 specification's sandbox logo settings section.
Harmful inline code
CSP is based on source whitelists, but it doesn't solve the biggest source of XSS attacks: inline script injection. If an attacker can inject script tags containing harmful code (<script>sendMyDataToEvilDotCom();</script>), the browser does not have a good mechanism to distinguish this tag. CSP can only solve this problem by completely forbidding inline scripts.
This prohibition includes not only script tags embedded in the script, but also inline event handlers and javascrpt: URLs. You need to put the contents of the script tag into an external file and replace javascript: and <a…onclick=[JAVASCRIPT]> with the appropriate addEventListener. For example, you might put the following form:
<script>
function doAmazingThings() {
alert('YOU AM AMAZING!');
}
</script>
<button>Am I amazing?</button>
Rewrite to the following form:
<!-- amazing.html -->
<script src='amazing.js'></script>
<button>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
alert('YOU AM AMAZING!');
}
document.addEventListener('DOMContentReady', function () {
document.getElementById('amazing')
.addEventListener('click', doAmazingThings);
});
Regardless of whether you use CSP or not, the above code actually has greater advantages. Inline JavaScript completely mixes structure and behavior, you shouldn't do that. In addition, outreach resources are easier to cache in browsers, easier for developers to understand, and are easy to compile and compress. If you use outreach code, you will write better code.
Inline styles need to be processed in the same way, both the style attributes and the style tags need to be extracted into the external stylesheet. This can prevent all kinds of magical data leakage.
If you have to have inline scripts and styles, you can set the 'unsafe-inline value for the script-src or style-src attribute. But don't do this. Forbidding inline scripts is the maximum security guarantee provided by CSP. At the same time, forbidding inline styles can make your application safer and more robust. It's a trade-off, but it's well worth it.
Eval
Even if an attacker cannot inject the script directly, he may induce your application to convert the inserted text into an executable script and execute it itself. eval() , newFunction() , setTimeout([string], ...) and setInterval([string], ...) can all become such dangerous carriers. The strategy for CSP to target this risk is to block these vectors altogether.
This has some implications on the way you build your app:
Parse JSON via built-in JSON.parse without relying on eval. Browsers after IE8 support local JSON operations, which is completely safe.
Rewrite the call method of setTimeout and setInterval by inline functions instead of strings. For example:
setTimeout(document.querySelector('a').style.display = 'none';, 10);
Can be rewritten as:
setTimeout(function () { document.querySelector('a').style.display = 'none'; }, 10);
Avoid inline templates at runtime: Many template libraries use new Function() to speed up template generation. This is great for dynamic programs, but it is risky for malicious text.
Report
CSP can block untrusted resources on the server side, which is very useful for users, but it is very useful for us to get various notifications sent to the server, so that we can identify and fix any malicious script injections. For this purpose, you can instruct the browser to send an intercept report in JSON format to an address through the report-uri directive.
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
The report will look like this:
{
csp-report: {
document-uri: ,
referrer: ,
blocked-uri: ,
violent-directive: script-src 'self' https://apis.google.com,
original-policy: script-src 'self' https://apis.google.com; report-uri
}
}
The information contained in it will help you identify the blocking situation, including the document-uri that occurs, the page referrer, the resource that violates the page policy, the violated directive, and the original policy of all the page content.
Realistic usage
CSP is now available on Chrome 16+ and Firefox 4+ browsers, and it is expected to receive limited support on IE10. Safari is not supported yet, but the WebKit nightly build is available, so it is expected that Safari will be supported in the iteration below.
Let's look at some commonly used use cases below:
Actual Case 1: Social Media Widget
Google +1 button includes scripts from https://apis.google.com, as well as iframes embedded from https://plusone.google.com. Your policy needs to include these sources to use the Google+1 button. The easiest strategy is script-src https://apis.google.com; frame-src https://plusone.google.com. You also need to make sure that the JS fragments provided by Google are stored in external JS files.
There are many implementation solutions for Facebook's Like button. I recommend you stick to the iframe version as it keeps well isolated from the rest of your site. This requires the use of the frame-src https://facebook.com directive. Please note that by default, the iframe code provided by Facebook uses the relative path //facebook.com. Please modify this code to https://facebook.com. There is no need for HTTP to not use.
Twitter's Tweet button depends on script and frame, both from https://platform.twitter.com (Twitter provides a relative URL by default, please edit the code when copying to specify it as HTTPS).
Other platforms have similar situations and can be solved similarly. I recommend setting default-src to none and then looking at the console to check which resources you need to use to make sure the widget works properly.
Using multiple widgets is very simple: just merge all policy directives and remember to put the settings of the same directive together. If you want to use the above three widgets, the strategy will look like this:
script-src https://apis.google.com https://platform.twitter.com; frame-src https://plusone.google.com https://facebook.com https://platform.twitter.com
Actual Case 2: Defense
Suppose you visit a banking website and want to make sure that only the resources you need are loaded. In this case, start setting a default permission to block everything (default-src 'none ') and build the policy from scratch.
For example, a bank website needs to load images, styles and scripts from the CDN from https://cdn.mybank.net, and connect to https://api.mybank.com/ through XHR to pull various data. It also needs to use a frame, but the frames are all from non-third-party local pages. There is no Flash, fonts, and other content on the website. In this case we can send the strictest CSP header is:
Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; frame-src 'self'
Actual Case 3: Use only SSL
A wedding ring forum administrator wants all resources to be loaded in a safe way, but does not want to really write too much code; rewriting a large number of third-party forum inline scripts and styles is beyond his ability. So the following strategy will be very useful:
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
Although default-src specifies https, scripts and styles are not automatically inherited. Each directive will completely override the default resource type.
future
The W3C's Web Application Security Working Group is developing details of the content security policy specifications. Version 1.0 will enter the final revision stage, which is very close to what is described in this article. The public-webappsec@ email group is discussing version 1.1, and browser manufacturers are also working hard to consolidate and improve the implementation of CSP.
CSP 1.1 has some interesting things on the artboard that are worth listing separately:
Adding policies through meta tags: The preferred way to set CSP is HTTP headers, which is very useful, but it will be more direct through tags or scripts, but it has not been finalized yet. WebKit has implemented the feature of permission setting through meta elements, so you can now try the following settings in Chrome: Add <metahttp-equiv=X-WebKit-CSP content=[POLICY GOES HERE]> to the document header.
You can even add policies through scripts at runtime.
DOM API: If this feature is added to the next iteration of CSP, you can query the page's current security policy through Javascript and adjust it according to different situations. For example, if eval() is available, your code implementation may be slightly different. This is very useful for the author of the JS framework; and the API specification is still very uncertain, you can find the latest iteration in the script interface section of the draft specification.
New Directives: Many new directives are under discussion, including script-nonce: Inline scripts can only be used if the explicitly specified script elements are used; plugin-types: This will limit the type of plugin; form-action: Allow form to be submitted to a specific source only.
If you are interested in discussions about these future features, you can read the archive of the mailing list or add it to the mailing list.
This article is translated from:
Excerpted from: Jiang Yujie's blog