Order of sources when using strict-dynamic - content-security-policy

Documentation reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic
The docs above state the backwards compatible way of working with 'strict-dynamic':
script-src 'unsafe-inline' https: 'nonce-abcdefg' 'strict-dynamic'
Is the difference with the example below that the 'nonce' value will take priority over 'strict-dynamic' in browsers that support CSP3 and therefore not apply the 'strict-dynamic' functionality:
script-src 'strict-dynamic' 'nonce-someNonce'

There is no priority. CSP level 1 didn't have nonces or strict-dynamic, level 2 did have nonces but not strict-dynamic. Level 1 is likely not relevant today. If a browser on CSP level 2, or partial level 3, sees 'strict-dynamic' it will simply ignore it and may show a console message that the keyword is unknown.

Related

Do Content Security Policies support a wildcard suffix for the top-level domain?

I have a Content Security Policy img-src I am unable to configure. The URL being requested has a dot country code suffix.
Example:
In Hong Kong = www.example.com.hk
In Thailand = www.example.com.th
Is there a way to add a wildcard to the end of a path? all the documentation I can find uses wildcards as a prefix.
I have tried www.example.com.* but it's an invalid source.
From MDN:
Internet hosts by name or IP address, as well as an optional URL scheme and/or port number. The site's address may include an optional leading wildcard (the asterisk character, '*'), and you may use a wildcard (again, '*') as the port number, indicating that all legal ports are valid for the source.
Nowhere does it say a wildcard can be used anywhere else.
While for you it may seem convenient in this specific context, in the bigger picture it would be a strange and probably dangerous thing to allow wildcarding the toplevel domain. Each toplevel domain is separately authoritative for the domains below it. There is no rule that says that www.example.com.hk and www.example.com.th must be owned by the same entity, and the same goes for the same-origin-policy. If that happens to be the case, it should be seen as coincidental.
You can use csp-evaluator to try out CSP values and see what they mean.

Content Security Policy granularity: Does 'unsafe-eval' apply globally to all scripts?

This is a CSP, the question relates to the script source element:
default-src 'none'; script-src 'self' 'unsafe-eval' https://maps.googleapis.com; style-src 'self' https://fonts.googleapis.com 'unsafe-inline';
So this CSP sets unsafe eval, does this setting apply to all scripts or only the Self scripts i.e. what level of granularity does unsafe eval apply?
It applies to all scripts.
The reason is, 'unsafe-eval' and 'self' are just different types of what the CSP spec calls a “source expression”, and the values of CSP directives such as script-src are what the CSP spec calls “source lists” — lists of separate individual source expressions.
And source expressions in a CSP source list have no internal association with each other — instead they each apply globally to the directive they’re associated with.
So if you specify 'unsafe-eval' for the value of a script-src directive, then that always has the effect of globally allowing eval() in any JavaScript code in the document relies on.
From https://w3c.github.io/webappsec-csp/#framework-directive-source-list:
Many directives' values consist of source lists: sets of strings which identify content that can be fetched and potentially embedded or executed. Each string represents one of the following types of source expression:
Keywords such as 'none' and 'self' (which match nothing and the current URL’s origin, respectively)
Serialized URLs such as https://example.com/path/to/file.js (which matches a specific file) or https://example.com/ (which matches everything on that origin)
Schemes such as https: (which matches any resource having the specified scheme)
Hosts such as example.com (which matches any resource on the host, regardless of scheme) or *.example.com (which matches any resource on the host’s subdomains (and any of its subdomains' subdomains, and so on))
Nonces such as 'nonce-ch4hvvbHDpv7xCSvXCs3BrNggHdTzxUA' (which can match specific elements on a page)
Digests such as 'sha256-abcd...' (which can match specific elements on a page)

Is it possible to use Content-Security-Policy to allow inline scripts from one host, but only external scripts from other hosts?

Is it possible to use Content-Security-Policy to allow inline scripts from one host, but only external scripts from other hosts?
I'd like to do something like:
Header set Content-Security-Policy script-src myhost.com 'unsafe-inline'; script-src someothersite.com
But obviously that's invalid.
Is it possible to use Content-Security-Policy to allow inline scripts from one host, but only external scripts from other hosts?
No, it’s not possible. Content Security Policy (by design) lacks any means to express that.
In particular, as far as CSP syntax goes, myhost.com and 'unsafe-inline' are both what the CSP spec calls a “source expression”, and the value of the script-src CSP directive is what the CSP spec calls a “source list” — that is, a list of separate individual source expressions.
And in a CSP source list, source expressions have no relation internally with each other — instead they each apply to the entire directive they’re part of. So if you specify 'unsafe-inline' for the value of a script-src directive, then that always has the effect of globally allowing inline scripts anywhere in the document, period.
So the gist of it is: CSP has no syntax to express “only allow inline scripts for myhost.com”.
The spec details are at https://w3c.github.io/webappsec-csp/#framework-directive-source-list:
Many directives' values consist of source lists: sets of strings which identify content that can be fetched and potentially embedded or executed. Each string represents one of the following types of source expression:
Keywords such as 'none' and 'self' (which match nothing and the current URL’s origin, respectively)
Serialized URLs such as https://example.com/path/to/file.js (which matches a specific file) or https://example.com/ (which matches everything on that origin)
Schemes such as https: (which matches any resource having the specified scheme)
Hosts such as example.com (which matches any resource on the host, regardless of scheme) or *.example.com (which matches any resource on the host’s subdomains (and any of its subdomains' subdomains, and so on))
Nonces such as 'nonce-ch4hvvbHDpv7xCSvXCs3BrNggHdTzxUA' (which can match specific elements on a page)
Digests such as 'sha256-abcd...' (which can match specific elements on a page)

CSP url-scheme http:// matches if resource's scheme is either http:// or https://

Let's take the frame-ancestors directive. Say I want to allow http://*.parent.com to load my page at both https://child.com and http://child.com:
If I set frame-ancestors https://*.parent.com we have an obvious violation
If I set frame-ancestors *.parent.com we have a violation for https://child.com.
If I set frame-ancestors http://*.parent.com it works for both cases. Why ?
Quoting from the CSP2 spec:
If the source expression has a scheme-part that is not a case insensitive match for url-scheme, then return does not match.
If the source expression does not have a scheme, return does not match if any of the following are true:
the scheme of the protected resource’s URL is a case insensitive match for HTTP, and url-scheme is not a case insensitive match for either HTTP or HTTPS
the scheme of the protected resource’s URL is not a case insensitive match for HTTP, and url-scheme is not a case insensitive match for the scheme of the protected resource’s URL.
Case 2 of my examples must choke according to condition 5.2 of the spec since https://child.com is not a match for HTTP, that I understand.
But why does case 3 work for https://child.com? According to condition 4 it should fail since http://*.parent.com does not match the scheme of https://child.com. What am I missing ?
Objective
Allow http://*.parent.com to load my page at both https://child.com and http://child.com
Case 3
CSP policy on child.com:
frame-ancestors http://*.parent.com
But why does case 3 work for https://child.com?
If you browse to, e.g. http://1.parent.com, which iframes some child.com content, e.g.:
<iframe src="https://child.com/mycontent">
....then child.com checks its CSP frame-ancestors expression against the URL requesting /mycontent, viz:
http://1.parent.com is compared to http://*.parent.com, which matches, so /mycontent is successfully returned.
According to condition 4 it should fail since http://*.parent.com does
not match the scheme of https://child.com. What am I missing ?
It sounds a bit like you may be misunderstanding frame-ancestors. In your example, there wouldn't be any checks against https://child.com. The check is made by child.com, comparing the URL requesting /mycontent to http://*.parent.com. Perhaps you're confusing frame-ancestors with frame-src?
As for the spec, I found it very difficult to understand, but fortunately I found this very helpful page which translates it into something more readily understandable. I'll quote the relevant bit:
5 - If the whitelisted host in the CSP does not have a scheme, the browser is allowed to:
1 - Loads assets using HTTP or HTTPS if the page is served using HTTP.
2 - Loads assets using HTTPS if the page is served using HTTPS.
From all the reading around I've done, omitting the URL scheme from the CSP expression should work to allow both http and https requests, implicitly. But in practice, with frame-ancestors, it hasn't worked for me....at all....and that's consistent across browsers too.
So, my practical experience of using CSP frame-ancestors has led me to believe that in order to grant access to sites so that they may iframe my site's content, I need to prefix the domain with the URL scheme in my site's CSP frame-ancestors expression.
Typically I would want to allow both http and https, therefore I would need, e.g.:
frame-ancestors http://*.parent.com https://*.parent.com

Why `mod_headers` cannot match based on the content-type?

I can't get my mind wrapped around the comments and way of coding, to set a header only for .html in for example the .htaccess file in html5 boilerplate.
The clue for a big codeblock lays in the fact that 'mod_headers can't match on the content type' (as # commented). So I wander 1: why is there a 'Header unset' in a <FilesMatch>, that just has been announced to be impossible?
<IfModule mod_headers.c>
Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
# `mod_headers` cannot match based on the content-type, however,
# the `Content-Security-Policy` response header should be send
# only for HTML documents and not for the other resources.
<FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|woff2?|xloc|xml|xpi)$">
Header unset Content-Security-Policy
</FilesMatch>
</IfModule>
I looked everywhere, but only land on pages that have the same, almost ritual used codeblock, without further explanation. So question 2: why is a simple declaration like this not possible?:
<IfModule mod_headers.c>
# Content-Security-Policy for .html files
<FilesMatch "\.(html)$">
Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
</FilesMatch>
# OR like this
<Files ~ "\.(html)$">
Header set Cache-Control "public, must-revalidate"
</Files>
</IfModule>
It is possible to do that as per your second example. But .html are not the only files that could be sent as documents. You could also use .php or .htm or any other number of files. Some of these (like .php) may execute and then ultimately return HTML but the server doesn't know that as all it knows at this stage is the file extension.
CSP should be set on all documents but, to save header bandwidth, does not need to be set on assets used by those documents (e.g. images, style sheets, javascript... etc). Other than wasted bandwidth there is no really harm in setting it on other documents.
Ideally you would set it based on the mime-type returned (which in the PHP example above would be HTML). So anytime a HTML document is returned then set it, else don't. However as this is not possible you're left with two choices:
Set it on everything by default but then explicitly unset it for known media types. This pretty much guarantees it will be set on the document but also risks it being set on a few other file types (e.g. if you don't explicitly unset it for that type) - which, as I say isn't really that bad.
Explicitly state your HTML document types (like your second example). The risk here is you miss a file type (e.g. .php) either now or when someone else starts using php on your site in future.
The first option is the safer option. Particularly for html5boiler plate where they have no idea what technology will be used on the site it's used on. A site might use .php, .cgi, .asp or any number of technologies to generate the HTML document. They could even proxy the request to a back end server so they would be no file extension.
Make sense?

Resources