Ghost icon indicating copy to clipboard operation
Ghost copied to clipboard

Feature: headers.yaml & default security headers

Open ErisDS opened this issue 5 years ago • 10 comments

In Ghost we have the ability to set a content type header for any custom route in routes.yaml, but otherwise, headers are not customisable.

There are many different headers that a user might want to set, the most common use case being to improve security. We should also provide a better default set of headers, as part of this.

Ref material

  • Netlify _headers file docs: https://www.netlify.com/docs/headers-and-basic-auth/
  • Ghost docs _headers file example: https://github.com/TryGhost/docs/commit/8af94d2c92d455738b940150cba56bf741ed9568
  • Statamic content type: https://docs.statamic.com/routing#content-types
  • Ghost content type: https://docs.ghost.org/concepts/routing/#content_type
  • Ghost CLI default headers: https://github.com/TryGhost/Ghost-CLI/blob/975049cd850f0cdf33555c73a2b675fe7b6e742e/extensions/nginx/templates/ssl-params.conf#L11

Spec

We will provide the ability to upload a headers.yaml file. The file must contain valid YAML.

Headers will apply to the web application of Ghost only (same as redirects).

We will allow for setting headers per route with route matching exactly like Netlify _headers

Format

The top level YAML keys represent rules, with wildcard matches. Nested key value pairs should represent header names and values. Case should not matter.

/*:
  Header-Name: Value

/something/*:
   header-name: value

Real world example (taken from Ghost docs):

/*:
  Referrer-Policy: no-referrer-when-downgrade
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  X-Content-Type-Options: nosniff
  X-Frame-Options: SAMEORIGIN
  X-Xss-Protection:	1; mode=block
  Feature-Policy: accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'

Blacklisted / Ignored Headers

There may be some headers that Ghost sets which should be ignored if they are overridden via headers.yaml, to prevent any conflicts or issues on Ghost(Pro). The first that comes to mind is Cache-Control & Proxy headers.

  • Cache-control
  • X-Forwarded-*
  • X-Ghost-*
  • Location?
  • ???

X-Powered-By

Express also automatically sets X-Powered-By to express. It's always been tricky for us to remove because the setting doesn't cascade, and we have 4 different express apps. As part of this task we should remove X-Powered-By: express from everywhere.

Default Headers

Ghost currently does not set any security headers. This task includes deciding on and implementing a set of defaults for the frontend, admin and API. Defaults for the frontend should then be included as the default headers.yaml file, so that they can be overridden and extended.

At bare minimum we should have defaults something like those Ghost-CLI already sets:

/*:
  Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload';
  X-Frame-Options: SAMEORIGIN
  X-Content-Type-Options nosniff;

Note: Because of the View Site feature in Ghost admin, the X-Frame-Options header should be set to allow-from admin.url if an admin url is set.

To determine a full set of defaults, we should review the headers set on docs: https://github.com/TryGhost/docs/commit/8af94d2c92d455738b940150cba56bf741ed9568 and the advisory info from securityheaders.com

If a user uploads a file without these defaults, they won't be set for the Ghost frontend.

It may be the case that the API & admin need slightly different or less default security headers, but we should still implement the best possible set we can without impacting users.


Tasks

  • [ ] Finalise header blacklist
  • [ ] Decide on default headers for each area of Ghost
  • [ ] Implement headers on admin & api
  • [ ] Fully remove x-powered-by
  • [ ] Implement frontend defaults & ability to override via headers.yaml file
  • [ ] Add UI for uploading headers.yaml

ErisDS avatar Sep 06 '19 13:09 ErisDS

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Dec 05 '19 14:12 stale[bot]

Not stale - note to self: need to update the bot to ignore the feature label

ErisDS avatar Dec 05 '19 18:12 ErisDS

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Mar 04 '20 19:03 stale[bot]

Not stale - still relevant.

KopfKrieg avatar Mar 05 '20 03:03 KopfKrieg

Hi there @ErisDS 👋🏽 I was thinking of giving this feature a try. I just wanted to double check if there has been any advancements or any decisions have been made concerning this issue? If not, I m gonna go ahead and figure out what all has to be done for this to advance. Cheerz.

piggydoughnut avatar Jan 20 '21 23:01 piggydoughnut

Hey @piggydoughnut :wave: There have not been any advances as far as I know of. Although I see the "pinned" label has been removed last year, so the stale bot should have closed this issue long time ago :grimacing: Will ping internally if the feature is still relevant!

naz avatar Jan 24 '21 22:01 naz

@naz Oh I see, thank you for that :) I thought I d ask first as it seems suspiciously old :) hah

piggydoughnut avatar Jan 24 '21 22:01 piggydoughnut

Is there still work being done on this feature? I would be interested to take it on.

twnk avatar May 13 '21 10:05 twnk

Is there still work being done on this feature? I would be interested to take it on.

My student group (me, dmitrymarokhonov and two others) took on this issue as part of a university course project, but we could not implement it successfully and I believe our pull request was rejected. So I can at least tell you that we aren't working on it anymore.

tuijapalovuori avatar May 13 '21 18:05 tuijapalovuori

@naz Is this issue still relevant? If yes, can I take up this issue?

kumarrus avatar Jan 09 '22 19:01 kumarrus

Hi @ErisDS! I see this ticket seems to be still open. I'm interested in picking up this issue but I wanted to confirm that this feature request is still live before I begin working. Have there been any changes to the current spec?

joeldesante avatar Jul 03 '23 20:07 joeldesante