Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Content Security Policy #7206

Closed
ScottHelme opened this issue Aug 16, 2016 · 15 comments

Comments

@ScottHelme
Copy link

commented Aug 16, 2016

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

This comment has been minimized.

Copy link
Member

commented Aug 16, 2016

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: #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

This comment has been minimized.

Copy link
Author

commented Aug 16, 2016

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

This comment has been minimized.

Copy link
Member

commented Aug 23, 2016

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

This comment has been minimized.

Copy link
Author

commented Aug 23, 2016

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

This comment has been minimized.

Copy link
Author

commented Aug 23, 2016

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

This comment has been minimized.

Copy link

commented Aug 23, 2016

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 #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 #3323, this is going to be one fairly complicated policy to flesh out if we want it to be concise.

@ScottHelme

This comment has been minimized.

Copy link
Author

commented Aug 23, 2016

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

This comment has been minimized.

Copy link
Contributor

commented Aug 23, 2016

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

This comment has been minimized.

Copy link
Author

commented Aug 23, 2016

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

This comment has been minimized.

Copy link

commented Aug 20, 2017

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

@kirrg001 kirrg001 added the needs info label Sep 8, 2017

@ErisDS

This comment has been minimized.

Copy link
Member

commented Oct 18, 2017

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.

@ErisDS ErisDS closed this Oct 18, 2017

@Zokormazo

This comment has been minimized.

Copy link

commented Nov 27, 2017

the {{ ghost_head }} tag generates an inline <script> block that makes imposible to set any CSP policy that protects the site from XSS attack. Would be possible to avoid that inline js and replace it with a src one or add the possibility to add a custom nonce tag to it?

We could just use clientSecret as nonce= value, so anyone can build a custom CSP policiy with it

@zackify

This comment has been minimized.

Copy link

commented Jan 23, 2018

@Zokormazo I completely agree... need to put it inside a script tag... for now have to hash it.

@JohnONolan

This comment has been minimized.

Copy link
Member

commented Mar 21, 2018

I'm going to summarise this and lock the issue, because this comes up every couple of months - and it never actually gets anywhere. So if you've come here from a link or a search and you're wondering: "Hey what's the deal with CSP support in Ghost? Why is there all this discussion yet nothing has actually happened?" -- This is your answer.

Here is the state of affairs when it comes to building CSP support (and things like it) in Ghost:

  1. Nobody in Ghost core is knowledgeable or passionate about CSP support, nor is it something which we receive a significant of demand for. Therefor: We are not directly working on it or concerned about it in any way. It isn't personal. We have just two full time salaried developers working on Ghost core. We can't do everything.

  2. With a closed platform, you have no recourse if the core team isn't focused on your pet feature. The conversation ends there. The entire point of open source is that if you are knowledgeable and passionate about something which would improve the software, then you are still able to implement it yourself. That is the freedom which open source grants you.

  3. If you choose to send your implementation upstream in the form of a Pull Request, and it is accepted as a good implementation which will work for all users and not just you, then others can benefit from your work.

  4. If you don't have the ability or the inclination to build a feature for your needs, then you can always pay someone else to build it for you -- commonly known as sponsoring an issue. Either you have a motivation to build it yourself, or you provide someone else with an economic motivation to build it for you.

  5. Not every feature makes it upstream. It may be that you build a great CSP implementation, and you're really happy with it but either you don't want to send it upstream, or we don't think it's a good fit for most users. That's ok too. Apple use Ghost internally to run hundreds of micro-sites for their different teams, but their own security policies meant that they elected to fork and rip out Ghost's user system and replace it with a SAML implementation that was compatible with their other systems. Totally fine. Not something which will ever make it upstream. But the fact that Ghost is open source meant they were able to do it.

It's strange, in a way, to have to explain all of this - because it should be implicit to participation in open source. Unfortunately though, a culture has developed over time which is well summarised in this (wonderful, but depressingly accurate) post by Nolan Lawson: https://nolanlawson.com/2017/03/05/what-it-feels-like-to-be-an-open-source-maintainer/

People show up to github with the expectation that, if a reasonable case is made for the legitimacy of their pet feature then the maintainer's moral obligation is to acquiesce and build it for them.

Everyone needs to understand that this is not how open source works.

We build the product we want to build. We do our best to prioritise and make good decisions, but we're far from perfect. We give away the rights to everything we release, so that you can build on top of it. That's how open source works.


Ok, so what's the state of CSP support in Ghost?

  • We have confirmed there is nobody on the core team working on it, which means it's open to contributions
  • A couple of implementations have been suggested above, and none of them have been viable for inclusion in core. This doesn't mean they can't be built. Just that we wouldn't merge them.
  • No suggestion or implementation has been made which is compatible with core so far, therefor: nothing has happened

Based on the discussion, Hannah has suggested:

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 [...] 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.

So if someone wants to build something which will be accepted upstream, then here are some good starting points / threads to pull on.

Meanwhile, there is a separate issue of the Ghost-API inline script conflicting with people setting up their own CSP: #7299. Yes this is valid, yes the core team will resolve it, and no it is not at all a priority because the API is in beta. It is absolutely expected to be experimental and broken. It even says that at the top of the labs page where the API is enabled/disabled from.

Could it be fixed and moved out of beta quicker? Absolutely. Scroll up to the beginning of this comment for instructions exactly how.

@TryGhost TryGhost locked and limited conversation to collaborators Mar 21, 2018

@JohnONolan

This comment has been minimized.

Copy link
Member

commented Mar 21, 2018

If anyone wants to make a new proposal for functional and viable CSP support in Ghost, that's totally fine and very welcome - you can start with a new issue and a clean slate. (Even better: start the discussion with a PR)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
9 participants
You can’t perform that action at this time.