directus icon indicating copy to clipboard operation
directus copied to clipboard

Self Hosted config options (helmet's adaptation)

Open triplecasquette opened this issue 1 year ago • 4 comments

Describe the Request

I think it would be nice to add at least:

OR

  • a more precise docs for CONTENT_SECURITY_POLICY_*
  • a doc page for how to setup livepreview for self hosted

because now (not some times ago) I had a CSP error with IFRAME.

So I spend hours to understand (not find it but I deducted it) that

CONTENT_SECURITY_POLICY_DIRECTIVES__FRAME_SRC="array:https://www.yourwebsite.com/"

In the .env file was the solution ^^

Maintainence Strategy

As often as the config option of CSP will change

triplecasquette avatar Feb 25 '24 17:02 triplecasquette

Thanks. we need a document for this

Th1nhNg0 avatar May 08 '24 03:05 Th1nhNg0

Had the same problem. Here is my not-expert description:

Directus Default CSP

By default the configuration for CONTENT_SECURITY_POLICY is highly restrictive, and this is a feature, because it provides more security without effort. The default CSP Response Header is:

content-security-policy: 
  script-src 'self' 'unsafe-eval';
  worker-src 'self' blob:;
  child-src 'self' blob:;
  img-src 'self' data: blob: https://raw.githubusercontent.com https://avatars.githubusercontent.com;media-src 'self';
  connect-src 'self' https://* wss://*;
  default-src 'self';
  base-uri 'self';
  font-src 'self' https: data:;
  form-action 'self';
  frame-ancestors 'self';
  object-src 'none';
  script-src-attr 'none';
  style-src 'self' https: 'unsafe-inline'

This header is sent as an HTTP Response Header content-security-policy for any Directus Admin page.

These directives define which domain/host a request can be sent to, by HTML elements present in the Directus Admin page. The CSP specs allows granular control, defining whitelist domains for every HTML element that can perform an HTTP request

<script src="..."/>    => script-src
<iframe src="..."/>   => frame-src
<object src="..."/>   => object-src
<img src="..." />       => img-src
...

If a frame-src directive is not defined, the fallback will be child-src, but it's recommended to use frame-src.

Like frame-src, every other directives, if not defined, will fallback to child-src as well, but THIS IS ONLY MY UNVERIFIED ASSUMPTION.

Directus Live Preview / Draft Mode

In Directus, to enable live preview mode, you go in Settings > Data Model > Select a Collection and find an input where you defined the URL of the draft page. Directus will inject this URL as the src attribute of the iframe used for Live Preview.

Here is an example of that URL:

http://localhost:3000/draft/page

The domain entered here must be reachable by the CSP policy, so you must explicitly define it. In this case you must define http://localhost:3000.

Define CSP for Directus Admin

You must use Environment Variables for that (or any equivalent based on your env, like docker-compose )

# .env

# frame-src
CONTENT_SECURITY_POLICY_DIRECTIVES__FRAME_SRC="array:http://localhost:3000/"

# child-src
CONTENT_SECURITY_POLICY_DIRECTIVES__CHILD_SRC="array:http://localhost:3000/"

...

Why this strange syntax ?

Directus uses helmet inside the Web Server so these env var will be passed to helmet.

This is how in an express app you define global helmet configration

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        "frame-src": ["'self'", "http://localhost:3000", "instagram.com"],
        "script-src": ["'self'", "http://localhost:3000"],
      },
    },
  })
);

Directus parse the env var, looks for the tail i.e. DIRECTIVES__FRAME_SRC and inject the value in the helmet config.

# .env
CONTENT_SECURITY_POLICY_DIRECTIVES__FRAME_SRC="array:'self',http://localhost:3000/,instagram.com" # array syntax 1
CONTENT_SECURITY_POLICY_DIRECTIVES__IMG_SRC="'self',http://localhost:3000/,instagram.com" # array syntax 2

# helmet config
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        "frame-src": ["'self'", "http://localhost:3000", "instagram.com"],
        "img-src": ["'self'", "http://localhost:3000", "instagram.com"],
      },
    },
  })
);

NOTE: 'self' refers the the actual page, and must be 'self' and not self. This is why it has double quotes.

tresorama avatar Oct 15 '24 08:10 tresorama