Ghost: Feature: headers.yaml & default security headers

Created on 6 Sep 2019  路  4Comments  路  Source: TryGhost/Ghost

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

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
feature help wanted

All 4 comments

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.

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

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.

Not stale - still relevant.

Was this page helpful?
0 / 5 - 0 ratings