TryGhost / Ghost

Independent technology for modern publishing, memberships, subscriptions and newsletters.
https://ghost.org
MIT License
46.83k stars 10.2k forks source link

Add support for Content Security Policy #7206

Closed ScottHelme closed 6 years ago

ScottHelme commented 8 years ago

Issue Summary

Content Security Policy (CSP) is a modern browser security feature that affords a site a considerable amount of protection against content injection attacks, mixed-content, data exfiltration and more. Deploying CSP can be difficult but with the addition of nonce support in CSP 2, Ghost could now make the deployment of CSP surprisingly easy.


Backwards compatibility shouldn't be major concern as there is a graceful fallback provided by CSP should a browser not be compliant with a later version of the specification. Take the following policy:

Content-Security-Policy: default-src 'self' 'nonce-abc123' 'unsafe-inline'

An older CSP 1 compliant browser will not understand the nonce value and simply allow the unsafe-inline to take effect and execute inline script on the page. A CSP 2 or CSP 3 compliant browser will support the nonce value which overrides unsafe-inline. In these browsers only inline scripts on the page that bear the nonce=abc123 attribute will be allowed to execute, providing us the full protection of CSP.


Requirements

Ghost needs to provide a way to inject the nonce value into theme files, something like {{nonce_value}}, and also into scripts or other elements than an author may add to a page in the editor, perhaps using a similar approach.

It would also be nice to be able to edit the CSP header from the Ghost Settings pages. Ghost could inject it's own CSP as a site is permitted to issue multiple CSP headers, and that would allow a site admin to issue their own CSP alongside for additional features that they may desire, but the single header approach is preferred.


Benefits

Apart from the obvious benefits of controlling locations that scripts can be loaded from, and which inline scripts may or may not be executed, there are several other features of CSP that could be a great benefit to Ghost users. Here are just a few:

upgrade-insecure-requests

The UIR directive instructs a browser to change any HTTP asset on a page to HTTPS before making the request. This helps to mitigate the risk of mixed-content warnings on a HTTPS site.

form-action

The form-action directive allows a host to control where a form on the site may be submitted to. This is a nice protection to control where the login form can send your Ghost user credentials.

report-uri

CSP reporting is a way to get real-time feedback from your policy by having your visitor's browser submit reports to you when the CSP is violated. This is a great way to find issues on your site.

ErisDS commented 8 years ago

Hi there @ScottHelme, I appreciate your interest in improving the security in Ghost. Have you read the previous (very old) issue about CSP where we tried to implement it when it first came out? It's here: https://github.com/TryGhost/Ghost/issues/332 There's a related PR.

It wasn't possible for us to get this to work, and therefore we dropped it waiting for support to be reliable enough.

Perhaps you could review this and provide some information on whether we're likely to run into the same issues again?

ScottHelme commented 8 years ago

Hey @ErisDS, I did read through the previous issues yes. It sounded like you were having problems with nonce support but there are graceful fallback mechanisms in CSP that mean we should be able to support any browser, regardless of whether they are CSP 1/2/3 compliant. This is why I provided the specific example above of a nonce overriding the dangerous unsafe-inline setting. I couldn't find examples of the policies that you were trying to issue, but if you have them to hand I can comment on them more specifically.

Having CSP support in Ghost would be an amazing step forwards. I blog and talk about CSP extensively, I run https://report-uri.io and I keep apprised of the latest developments in the specifications. I'd be happy to work towards bringing CSP support to Ghost.

ErisDS commented 8 years ago

Hi @ScottHelme if you click through to #332 you should be able to see several different PRs with different attempts to add CSP support. With the various problems we ran into with nonces and needing to whitelist so many services, it seemed like a bad idea.

Again, this was super early on in the lifecycle of CSP, so if you have ideas for how we can approach this differently I'm all ears. Our editor is a very special case 😉 so I'd love to hear this explained much more contextually "for Ghost".

ScottHelme commented 8 years ago

I've done some testing with a basic policy that blocks inline script and I can't see any errors being thrown while I navigate the /ghost section of my blog. Has inline script been removed and can you confirm my findings?

To deploy wider protection for scripts and styles, the 2 content types we're probably most worried about, the ability to inject nonces as I mentioned above would be required. This provides theme creators and content authors an 'easy option' to bring in CSP support with minimal friction.

For widespread adoption and protection the following policy would actually be quite beneficial:

Content-Security-Policy 
default-src * 'unsafe-inline' 'unsafe-eval'; 
script-src 'nonce-abc123';
style-src 'nonce-abc123';

This means the blog can load any content it likes, except scripts and styles which have to have the nonce attribute. There is no name based whitelist required so nothing to build or maintain. The unsafe-inline also provides a safe fallback. I will get some feedback on the validity of that policy, but I guess I need to do some real world testing to see what Ghost needs

ScottHelme commented 8 years ago

cc @oreoshake @mikewest @marumari

What do you guys think of something like the above policy? I'm trying to think of a way to get wide support for CSP with minimal effort. Input welcome :-)

oreoshake commented 8 years ago

Having glossed over the majority of the references here, my only drive by comment is that the policy you suggest doesn't work as intended based on my understanding. Were you going for something like this?

default-src *; 
script-src 'nonce-abc123' 'unsafe-inline' 'unsafe-eval';
style-src 'nonce-abc123' 'unsafe-inline';

Any CSP is better than no CSP in my opinion. However, I'd drop the nonce from style-src since it's my (possibly outdated) feeling that restricting inline styles is not realistic as it works today.

Based on what I see in https://github.com/TryGhost/Ghost/pull/3323 and @ErisDS's comment about whitelisted so many services, I don't think this policy will work without adding * to style/script-src.

Maybe a compromise for the first pass is better:

default-src https: data:; 
object-src 'none';
script-src 'nonce-abc123' 'unsafe-inline' 'unsafe-eval' https:;
style-src 'nonce-abc123' 'unsafe-inline' https:;

Disables flash, kills mixed content, restricts inline scripts, disables JSONP, disables content sniffing script attacks, but allows eval.

With the 3rd parties listed in https://github.com/TryGhost/Ghost/pull/3323, this is going to be one fairly complicated policy to flesh out if we want it to be concise.

ScottHelme commented 8 years ago

Yeah, I was trying to think of a way that Ghost could deploy this without users having to have crazy levels of 3rd party whitelisting to consider. Basically, the content author can just put a nonce attribute into their script tags to load them in and Ghost would take care of it.

<script nonce="{{csp_nonce}}" src="example.com">

With this approach the author would have no whitelist to maintain and can simply include external scripts or styles using this nonce approach. Maximum flexibility and minimum effort. For the rest of the content types I can live with limited coverage, heck use a *, if we can protect scripts then we have a big win. Things like upgrade-insecure-requests then become easy wins for a single button press in the GUI if the site is HTTPS.

My concern is how to handle the graceful fallback. In the example you gave above, to my understanding the nonce would override the unsafe values in script-src in CSP 2+ or in CSP 1 the browser could use both unsafe and HTTPS. The only problem is, even in a CSP 2+ compliant browser, wouldn't it be able to match on https: or the nonce? The spec states:

Script elements can then execute either because their src URLs are whitelisted or because they have a valid nonce:

This was my reason for pushing the unsafe values and any scheme or wildcard source out of script-src. A CSP 2+ browser should honour the nonce and CSP 1 will fallback to default. That's my thought anyway, Further input welcome :-)

kevinansfield commented 8 years ago

Sorry if this is nonsense as I've had a quick look at the use of nonces in CSP but another issue to consider is that many blogs will serve cached content with pages only being served by the Ghost service once when a post is published/updated and then served as "static" pages from a cache server/CDN - that effectively makes the nonce attributes useless as they only supply security if they are re-generated on every page view.

ScottHelme commented 8 years ago

This really depends on the architecture but either way, I'm not sure if there is a problem or not.

If the page is saved as a static item in the cache then yes it would be served with a static nonce in the script/style tags but the nonce would have to be persisted in the header too. The only way an attacker could abuse this is if they could somehow inject content into the cached copy, is it not? Any call back to the origin for a fresh copy of the page would result in a new nonce.

The other scenario is that all content is cached at the edge but the page request itself is still generated by the origin. In this case the nonce would work fine and the browser could happily load the content from cache, local or otherwise.

I think that makes sense...

Have-Purple commented 7 years ago

Any movement on this issue? Seems we have a circular conversation. #7206 => #277 => #7206

ErisDS commented 6 years ago

This discussion has hit a bit of a dead end. There's no one in the Ghost core team advocating for, or who really has a full understanding of CSP, meanwhile the suggestions for how to do it don't seem to take into account Ghost's full feature set.

I personally don't understand how CSP can ever work with something like Ghost, which has features like the editor and code injection.

There have been several suggestions for policies that might work, but none of those suggestions have clearly explained how they would work around the problem of the editor and code injection allowing users to inject all manner of scripts and styles.

Quite simply, I don't think there is any global CSP that would work for Ghost, and what is needed is some concept of allowing security-conscious blog owners to set this up for their own blogs through configuration or some other kind of extension.

I'm going to close this issue for now. If anyone has an idea for a way to do this and can explain how it will work given Ghost's features, we're all ears. If anyone has an idea for how to do it as config, also interested. Otherwise it's probably a case of holding on for a mechanism for providing custom middleware.

Zokormazo commented 6 years ago

the {{ ghost_head }} tag generates an inline Githubissues.

  • Githubissues is a development platform for aggregating issues.