I want to define a content security policy that allows loading images from any origin by default but restricts this to allow only a specific set of origins in some sections of the website.
In a traditional website that makes a new HTTP request for every navigation, this could be easily done by sending a different Content-Security-Policy HTTP header for the pages that require the stricter policy. But in a single page application, this is of course not possible because navigating to a more restrictive section of the app does not cause a new HTTP request (also I would like to define policies on more dynamic conditions than a URL navigation).
I know that—besides in an HTTP header—CSP policies can also be defined in a meta tag and when multiple CSP policies are defined a request must pass all of them to be permitted. So my first approach to solving the problem was setting a default CSP in a Content-Security-Policy header for the entire page and then dynamically set more restrictive policies by adding a <meta http-equiv="Content-Security-Policy" content="…"> tag to the document's head when required.
And this works just fine for dynamically adding more restrictive policies. The big problem is that removing that meta tag or modifying it does not remove or modify the associated content security policy (tested in Chrome in Firefox). This behavior is defined in the W3C Content Security Policy spec:
Note: Modifications to the content attribute of a meta element after the element has been parsed will be ignored.
So is there any way to dynamically add (and more importantly also remove) a content security policy that does not rely on a HTTP navigation? I would like to avoid setting a restrictive image policy by default and then excepting individual images through hashes or nonces as this would be quite elaborate to implement.
In SPA you can each time to create a new fullscreen iframe and fill it by script. <iframe>, as nested browsing context, can have own CSP meta tag regardless of the parent page.
The parent page will contains only script to manage iframe's content, may be it's possible to use Worker() for this purpose.
Related
We're using the WordPress REST API to power a static site. The site is "headless" in the sense that we don't use a WordPress theme; we rely on the content being exposed via the REST API.
Some of the default Gutenberg blocks - the Buttons block for instance - have styles with hashed class names associated with them that don't seem to be exposed in the API. This would be kind of ok if the class names were predictable but, since they aren't, we have no way of providing the styles on our end.
If we do render the blocks in a theme, the styles are rendered in the footer
Here's an example of the style block for the default Buttons block looks like in a WordPress theme
The Rest API endpoint exposes the markup in content.rendered (including the classnames) but no styles
Is this expected behavior for using Gutenberg and the WordPress REST API? If so, is the correct solution to expose the styles via a custom field (for lack of a better term) on the API?
The unique id (hash) in the classnames are randomly generated each time the blocks are parsed, even when directly calling the REST API. Unfortunately, the inline style attributes like .alignleft are absent from the content markup in the REST API. Being a REST API, it makes sense that style specific information isn't included; this keeps data and presentation of the data separate. It also prevents bloating the API by including style-specific information that would be rarely used outside of WordPress theme.
In your scenario, if you wish to style the resulting HTML content without worrying about the unique id, I'd suggest using css partial selectors, eg:
div[class*="wp-container-"] .wp-block-button{
...
}
Alternatively, as you mentioned, its possbile to extend the REST API to include the styles. While I haven't built a working example of this for styles, when blocks where introduced I ended up extended the REST API to include extra meta data. I'd suggest looking at render_block_data to handle adding in the styles into the content.
Eg. For the buttons block, the serialized content stored in the database as:
<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons"><!-- wp:button -->
<div class="wp-block-button"><a class="wp-block-button__link">Hello</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->
By using parse_blocks() you can obtain all the block properties into an array and get style information that way. I think this approach is do-able if you just add the generated classnames and not the inline styles. I am keen to know if you find a better way...
From the OWASP's website
https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html:
Send a Content-Security-Policy HTTP response header from your web server.
Content-Security-Policy: ...
Using a header is the preferred way and supports the full CSP feature set. Send it in all HTTP responses, not just the index page.
I don't understand how that could be true as it is possible to set the Content-Security-Policy by using a meta tag in the HTML.
I also don't see how the policy can apply to anything else but HTML pages.
Does anyone have idea why that statement above was made and if it is safe to only send HTTP header Content-Security-Policy for text/html responses?
By the way, the policy is too big and I would like to sent as fewer bytes as possible.
This is still something that’s not formally specified and there ai still some debate on this: https://github.com/w3c/webappsec/issues/520
In general there’s two arguments here:
On the one hand some other file types (XML, PDF, perhaps even SVGs) could benefit from CSP and any resource could become the page by right clicking and opening in a separate tab.
On the other hand CSPs can get quite big and are usually written for HTML pages. So a bit wasteful to send on other resources and most of it won’t be relevant.
The right answer (as suggested by above) is probably to have a reduced, and very strict, CSP for all non-HTML responses.
But I think for most people having it on the HTML only will be good enough and bring most of the benefits of CSP. Then again CSP is an advanced technique so if going as far as that, then why not do it properly?
Using a header is the preferred way and supports the full CSP feature set.
I don't understand how that could be true as it is possible to set the Content-Security-Policy by using a meta tag in the HTML.
Inside the meta tag are not supported the directives:
report-to and report-uri
frame-ansectors
sandox
Also meta tag does not support Content-Security-Policy-Report-Only feature, only the Content-Security-Policy.
All resources that start loading before meta tag in the HTML code are not affected by CSP. Malicious scripts can be injected as first item of the <head> section just before meta tags
The nonce-value is exposed in meta tag therefore can be easely stealing by script and reuse.
Using meta tag you can only set the CSP for HTML pages, but CSP is applied for XSLT in the XML pages, and for some other kinds of content (see below).
Therefore indeed an HTTP header is the preferred way to delivery CSP and using CSP via meta tag does not allow you to use full CSP feature set.
Send it in all HTTP responses, not just the index page.
I also don't see how the policy can apply to anything else but HTML pages.
The specification had in mind a little different - you should send CSP with any response page with HTML content, not only for 200 OK, but even for 404 Not found
403 Access Forbidden, etc.
Because these pages has access to cookie that can be steal in the page not covered by CSP.
CSP is applied not only to HTML pages, but to XSLT in XML-pages, to external javascripts files for workers (in Firefox). Also frame-ancestors directive of CSP HTTP header applies to any content (JPEG/GIF/PNG/PDF/MP4/etc) intended to be embedded into iframe, see the nitty-gritty here.
I am getting the below error while running the application
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/ 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' 'sha256-5uIP+HBVRu0WW8ep6d6+YVfhgkl0AcIabZrBS5JJAzs='". Either the 'unsafe-inline' keyword, a hash ('sha256-4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ='), or a nonce ('nonce-...') is required to enable inline execution.
Below is the code currently I am using
const string modernizrHash1 = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
const string modernizrHash2 = "sha256-5uIP+HBVRu0WW8ep6d6+YVfhgkl0AcIabZrBS5JJAzs=";
app.UseCsp(options => options
.DefaultSources(s => s.Self())
.ScriptSources(s => s.Self().CustomSources("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/"))
.StyleSources(s => s.Self().CustomSources("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/", modernizrHash1, modernizrHash2))
.FontSources(s => s.Self().CustomSources("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/"))
.ImageSources(s => s.Self().CustomSources("data:"))
);
The hash 4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ= is for the CSS word-wrap: break-word; white-space: pre-wrap;, the inline style Chrome automatically applies when you are not serving a HTML document;
Example server response:
Content-Type: text/plain; charset=utf-8
Content-Length: 9
Content-Security-Policy: default-src 'self'
Date: Thu, 04 Nov 2021 11:33:49 GMT
some text
DOM in Chrome
<html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">some text</pre></body></html>
Console error
Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self'"
. Either the 'unsafe-inline' keyword, a hash ('sha256-4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.```
Adding the hash to default-src or style-src won't help because it is an "inline style" and "hashes do not apply to style attributes".
The best fix is to make sure you serve a proper HTML document with your CSP header, so browsers don't decorate it with their own styling.
You are getting this error because the inline styles (which can be hashed to sha256-4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ=) aren't allowed, as per your Content Security Policy and the error that is returned.
I would recommend two possible steps, either:
Move away from inline styles (as they can be insecure) and will require you to change your CSP each time that the inline styles change.
Add the supplied SHA to the StyleSources for your CSP. But Be aware that this will have to be maintained and updated for each inline style added across all pages in your application.
UPDATE based on comments below answer
Note: There is no issue when i am running it in IE browser, only in the chrome and firefox i am getting the issue
Taking a look at Can I Use shows that IE has only partial support for Content Security Policy, which would explain why you aren't seeing this error in the console. i.e. Internet Explorer doesn't support CSP, so it isn't applying it.
I have fixed the issue by changing the hash keys shown in the browsers console error, I have replaced the console error hash key with my code hash key, but not sure whether this is the permanent solution or not
This is not really a permanent solution. If (and when) your inline styles change, you will have to change the hash in your CSP in order for the styles to be applied again.
Which leads me on to:
Could you please guide me how the hash keys are getting generated and how i can fix this permanently
Looking at the code in your original question:
const string modernizrHash1 = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
const string modernizrHash2 = "sha256-5uIP+HBVRu0WW8ep6d6+YVfhgkl0AcIabZrBS5JJAzs=";
// other things
.StyleSources(s => s.Self().CustomSources("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/", modernizrHash1, modernizrHash2))
You are telling the browser (via the Content Security Policy that you are generating) that it is only permitted to load styles from:
self (the origin domain of your site)
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/
and those which match the following hashes:
sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
sha256-5uIP+HBVRu0WW8ep6d6+YVfhgkl0AcIabZrBS5JJAzs=
You mention that you've also included the hash:
sha256-4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ=
When the browser starts to render the page, it will look at the resources that the page requires in order to render and will compare it to the list in the Content Security Policy. If a resource (inline styles and inline javascript are classed as a resource) is not in the list, then the browser will do a simple SHA-2 of the contents of the resource and compares it to any of the hashes listed in the CSP.
Note: the same thing can be achieved by the use of a nonce
If the resource isn't from an allowed source, or it's hash does not match those listed in the CSP, then the browser will actively refuse it.
Because the SHA-2 algorithm uses the content of the resource to generate the hash, when the contents of it changes the calculated value of the hash will be different.
For example, the inline style here:
<p style="color: red;">hello, world</p>
might hash to F1FF77E5DDBB1AF52EB51C98F725927143221549385937595112128987CF39E4 (which is the hash of "color:red")
whereas the following inline style:
<p style="color: green;">hello, world</p>
might has to 2F262B22412B633D12B27FA9F94A3B0495821CB8341CFF0A88C80E3FED5DC9E8 (which is the hash of "color:green")
As you can see, there is a massive difference in the hashed styles. This is by design (in the algorithm).
To stop alleviate this problem, I would swap the inline styles for a css file describing how the content of the page should be styled. As long as the css file is served from the same origin as your HTML, then the self rule will cover it.
CSP is a pretty complex topic to pick up (but easy to master, once you have learned the basics). I would recommend taking a look at the MDN page for Content Security Policy for more information on how it works and how you can use it to secure your web applications.
I added the Content-Security-Policy header to our servers' responses, but the browser throws errors when Google Tag Manager (GTM) injects Custom HTML tags.
CSP3 has 'strict-dynamic' which appears custom-made for GTM, but currently only Chrome supports it, and CSP3 is a Working Draft.
I wrote code to get the Custom HTML tags via the GTM API, hoping I might take the scripts' hashes and add them to the header, but I found that the JavaScript provided by the API didn't match what GTM was injecting into the DOM, because GTM minifies/obfuscates the scripts before injecting them.
Now I'm wondering if maybe, just maybe, there's a way to tell GTM to add a nonce to each script it injects, but I can't find any documentation to support this hope/fantasy.
Has anyone run into this, and found a way to fix this?
(I can't just extract the hosts from the scripts as mentioned here - How to make Google Tag Manager and Content-Security-Policy coexist? - because the Custom HTML tags added by our Marketing Team are tags with non-empty bodies, not just tags with a source attribute and no body.)
I am using only HTTP and set "meta http-equiv="Cache-control" content="public""
in my head. How can I set the max-age of an element? Do I need to wrap it in a certain tag or would I use an attribute? I would like to set the max-age of my css and js sources to a certain max age.
Every resource being loaded is a separate request. If you want to control the caching of your JS and CSS files, you'll have to set the appropriate headers for those requests. Your web server should have a way to do that - if not, you'll just have to write your own handlers to add the headers as needed.
There is nothing you can do in HTML to achieve this, short of inlining the files.