kindspells / astro-shield

Astro integration to enhance your website's security with SubResource Integrity hashes, Content-Security-Policy headers, and other techniques.
https://astro-shield.kindspells.dev
MIT License
57 stars 6 forks source link

Support CSP headers generation for static content on Netlify #65

Closed castarco closed 2 months ago

castarco commented 7 months ago

To improve the usability of Astro-Shield, it would be good to support the generation of headers metadata specific for the Netlify provider, so we can generate CSP headers for static content.

Reference

Upvote & Fund

Fund with Polar

dsmith-digitalmint commented 2 months ago

@castarco thank you for working on this issue. I am having trouble getting it to work though. Is there the ability to configure the path of the _headers file? Here is my current setup:

public/headers:

# _headers
/* 
  X-Frame-Options: DENY
  Cross-Origin-Embedder-Policy: require-corp
  Cross-Origin-Opener-Policy: same-origin
  Cross-Origin-Resource-Policy: same-origin
  X-Robots-Tag: index, follow
  Referrer-Policy: no-referrer
  X-Content-Type-Options: nosniff
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  Permissions-Policy: geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()

astro.config.mjs:

import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap";
import icon from "astro-icon";
import {shield} from '@kindspells/astro-shield'

export default defineConfig({
  site: 'my-site',
  integrations: [
      mdx(), 
      tailwind(),
      icon(),
      sitemap(),
      shield({}),
  ],
});

It looks like my headers file was properly consumed: Screenshot 2024-09-19 at 1 55 59 PM

but it looks like the CSP isn't being properly added reference

What should I do?

castarco commented 2 months ago

Hi @dsmith-digitalmint , it works like this:

  1. You can have a _headers file in your public/ directory, where you specify some headers. This is optional. But in case you do, the syntax is the one specified in https://docs.netlify.com/routing/headers/#syntax-for-the-headers-file , it would be something like...
    # a path:
    /templates/index.html
     # headers for that path:
     X-Frame-Options: DENY
     X-XSS-Protection: 1; mode=block
    # another path:
    /templates/index2.html
     # headers for that path:
     X-Frame-Options: SAMEORIGIN
  2. When you run astro build, if Astro-Shield is configured as stated in the documentation ( https://astro-shield.kindspells.dev/guides/hosting-integrations/netlify/ ), it will either:
    • "patch" the _headers file after it is copied from public/ to dist/
    • or create a new one in case you opted for not having a _headers file in your public/ directory.

From my tests, Netlify takes care of running astro build for us and reading the patched/generated _headers file, without requiring from us any extra step such as committing the patched/generated file.

There is one strange detail, though, that is a bit confusing to me (I'm not even sure if the behavior is correct or not, as I didn't had time to check it thoroughly):

Now, I've tested this workflow for just one day and a half, so I think it is quite plausible that there might be some holes in it, or some mistakes in my understanding.

There are some things that you could check:

castarco commented 2 months ago

@dsmith-digitalmint

P.S.: Ah, I see, it's missing a couple of entries in the configuration object.

export default defineConfig({
  site: 'my-site',
  integrations: [
      mdx(), 
      tailwind(),
      icon(),
      sitemap(),
      shield({
        securityHeaders: {
          // This is required to work with Netlify
          enableOnStaticPages: { provider: "netlify" },

          // This is required to ensure that the CSP headers are generated
          contentSecurityPolicy: {},
        }
      }),
  ],
});

Also, well, this is enough for 100% static pages. To generate the headers for SSR content there are some extra knobs to turn, I hope that the documentation in https://astro-shield.kindspells.dev is enough 🤓 . If not, let me know.

I know it would be great if this worked out of the box without the extra bits of configuration, but the Netlify part is necessary to ensure that we can also do the same for other providers (such as Vercel), and the CSP part is necessary because the roadmap includes support for more security-related headers and we need to provide some flexibility.