Skip to content

Conversation

@ematipico
Copy link
Member

@ematipico ematipico commented May 13, 2025

Summary

export default defineConfig({
	security: {
		csp: true
	}
})

Links

@ematipico ematipico force-pushed the feat/rfc-csp branch 3 times, most recently from bbed778 to 85545c3 Compare May 13, 2025 12:21
@ematipico ematipico changed the title feat: Content Security Policy Content Security Policy (CSP) May 13, 2025
@ematipico ematipico marked this pull request as ready for review May 13, 2025 12:21
@NepCono
Copy link

NepCono commented May 13, 2025

@ematipico everybody is calling hashes, this can be nice, however wouldn't be nice to use a simple nonce generated by the user. and that the user can assign it to for instance Astro.cspNonce. In that case it would be very easy I guess ?
I wish I had more time, than I could develop with it.

But in the meantime will there be a possible workaround ?

@ematipico
Copy link
Member Author

@NepCono in the RFC I explained why nonce wouldn't work for Astro. Did you read that part?

@NepCono
Copy link

NepCono commented May 15, 2025

@ematipico just read about the nonce, very understandable :-) I was just saying that the nonce solution would be more simplified than the hash.

However still a question remaining would the out of the box implementation security > csp and strictdynamic > true that we dont have to insert a nonce on the script (not a server island:script) which will lead to using typescript instead of javascript ?

btw; when will this be released and can I access it earlier ?

Comment on lines 40 to 44
Include any useful background detail that that explains why this RFC is important.
What are the problems that this RFC sets out to solve? Why now? Be brief!

It can be useful to illustrate your RFC as a user problem in this section.
(ex: "Users have reported that it is difficult to do X in Astro today.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the default template, we can probably extract things from the original discussion


- Provide *multiple* solutions to fix the same problem
- Support for third-party scripts dynamically imported by users e.g. `document.createElement("script")`, `import("https://path.to/script.js")`;
- Support for third-party styles dynamically imported by users.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I undestand correctly, injectCspAsset would allow this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to rephrase it then. What I meant is that out of the box Astro won't create a hash for the dynamically imported script/style. Any suggestions?

Users can add an entire directive. Astro won't check if the directive is grammatically correct.

```js
Astro.insertCspPolicy(`image-src '${Astro.site}'`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this API covers injecting hashes directly eg.

Astro.insertCspPolicy(`script-src 'self' '${myHash}'`)

I like this, it's better then my proposal on Discord to have it on insertCspAsset

Copy link
Member Author

@ematipico ematipico May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we can't provide such API, in fact I think I will try to make it more strict. Astro must control the script-src and style-src directives, because that's where we append all the hashes. If a user provides their own directive, they override ours, and it's all for nought


# Drawbacks

The hashes solution will negatively impact the rendering engine because Astro will have to calculate the hashes **at runtime**. The most expensive solution is the support of server islands because the contents of their scripts depend on props and slots, which are only known during the rendering phase.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know how big the impact is? Genuinely curious

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but we can set up a codspeed benchmark

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a comparable solution, it would take around 500ms for dozens of pages in a Netlify Free tier build. This should be the base performance

@ematipico
Copy link
Member Author

I changed the APIs for additional directives. I believe the new API is better because counts for better DX:

cc6d3bf (#1168)

@NepCono
Copy link

NepCono commented May 16, 2025

We also have to keep in mind, that in order to work out of the box.

If I am correct the CSP policy will be configured in astro.config.mjs and that the CSP is handled by Astro.
That we also want something like whitelisted domains, for instance for "connect-src" default value will be "self".
but one must have the possibility to add "api.example.com"

@ematipico ematipico self-assigned this May 21, 2025
@louisescher
Copy link
Member

As discussed in the API bash, view transitions break when using the CSP settings without setting strictDynamic to true. I think we should log a warning if the user tries to use the client router while strictDynamic is not set to true?

```


#### `Astro.insertScriptHash(string)`
Copy link
Member

@florian-lefebvre florian-lefebvre May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to expose utilities to compute the hash, so it can be fed to this function eg.

import { generateHash } from 'astro/csp'

const hash = await generateHash(content, 'SHA-256')
Astro.insertScriptHash(`sha256-${hash}`)

Naming is bad but you get the idea

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this API isn't useful, as it essentially creates some unusual usage. Let's say you have some script.js somewhere. What would happen is the following:

  • The user must import script.js somewhere, and provide its content as a string to the generateHash
  • The generated hash must be saved or sent somewhere
  • Then the user must provide this hash to Astro via configuration or runtime
  • This very script must be added to the application

Let's wait and see.

Copy link

@Johannes-Andersen Johannes-Andersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on the proposal! Really exited with the new Header and adapter support 🙌

It could be a benefit to document this:

Authors are strongly encouraged to place meta elements as early in the document as possible, because policies in meta elements are not applied to content which precedes them. In particular, note that resources fetched or prefetched using the Link HTTP response header field, and resources fetched or prefetched using link and script elements which precede a meta-delivered policy will not be blocked.

https://www.w3.org/TR/CSP3/#meta-element

Would also be valuable to make sure the Astro puts it on the top of head?
Seems from my quick testing it is added at the bottom of <head />:
Screenshot 2025-06-11 at 14 15 21


The mapping is defined as `Map<IntegrationResolvedRoute, string>`, and it's the list of **static routes**, where:
- `IntegrationResolvedRoute` is the route type provided to the integrations;
- `string` is the **content** of the entire CSP header e.g. `"script-src: 'self' sha256-yadayada; style-src: 'self' sha256-yadayada`
Copy link

@Johannes-Andersen Johannes-Andersen Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `string` is the **content** of the entire CSP header e.g. `"script-src: 'self' sha256-yadayada; style-src: 'self' sha256-yadayada`
- `string` is the **content** of the entire CSP header e.g. `"script-src 'self' sha256-yadayada; style-src 'self' sha256-yadayada`

Directive should not have a : between key and value

Co-authored-by: Johannes Andersen <[email protected]>
@ematipico
Copy link
Member Author

ematipico commented Jun 11, 2025

Thank you @Johannes-Andersen, I applied your suggestions. It makes more sense to have the user write their format (apostrophe VS no apostrophe). Since this is an advanced API, we can reasonably assume that users are familiar with its usage. I will have to send a PR to change the APIs.

@ematipico
Copy link
Member Author

@stefanmaric Thank you for your suggestion. Unfortunately, this API wouldn't play well in Astro, where we need more control over the script-src and style-src directives. Also, in the future, we might need to have control over other directives too.

@HannesOberreiter
Copy link

Great implementation as far as I can tell. Two things I want to throw into the discussion:

Naming

Currently the Runtime APIs do not really reflect in their naming that they have something to do with CSP. Maybe they need a kind of prefix, eg. Astro.cspInsertStyleResource

Disable one of the two

Currently it is only possible to activate both script-src and style-src but not only one. Additionally to this, I would love a Runtime API which does completely overwrites the directives and not only add to resources/hashes (maybe an option flag for the current APIs, eg. overwrite = true). The reason on some pages I use legacy external scripts which apply a lot of inline css, and I can't be the only one with this problem.

@ascorbic
Copy link
Contributor

Or maybe Astro.csp.insertDirective() etc. Most other features are on an object, not at the top level

@florian-lefebvre
Copy link
Member

If in your Astro config, you have directives: ["img-src 'self'"] and you call Astro.insertDirective("img-src whatever"), what happens? Does it override, merge, or add (which is incorrect)?

@Eveeifyeve
Copy link

Eveeifyeve commented Aug 27, 2025

I have found issues with csp specifically devtools. Idk if this is known, but I thought I would post it anyways.

image

@ascorbic
Copy link
Contributor

@Eveeifyeve can you share a reproduction?

@ematipico
Copy link
Member Author

@Eveeifyeve

This is expected. CSP doesn't work in development. It's explicitly stated in this RFC and the documentation

@ematipico
Copy link
Member Author

If in your Astro config, you have directives: ["img-src 'self'"] and you call Astro.insertDirective("img-src whatever"), what happens? Does it override, merge, or add (which is incorrect)?

Since it's an insertion, this API will add an item to the existing array.

If that's incorrect, what do you expect from the API?

@ascorbic

This comment has been minimized.

@florian-lefebvre
Copy link
Member

I would expect it to merge, like what we did for fonts basically

@ematipico
Copy link
Member Author

ematipico commented Aug 28, 2025

I would expect it to merge, like what we did for fonts basically

Why though? I'm not saying it's incorrect, but font-src is particular because Astro needs to own font-src. If Astro doesn't own the other resources, why should Astro deduplicate the values that aren't owned?

@ematipico

This comment has been minimized.

@florian-lefebvre
Copy link
Member

Why though? I'm not saying it's incorrect, but font-src is particular because Astro needs to own font-src. If Astro doesn't own the other resources, why should Astro deduplicate the values that aren't owned?

Sorry I was on phone so it wasn't very clear. My point is not about deduplication but rather avoiding loosing directives. Taking my example again:

If in your Astro config, you have directives: ["img-src 'self'"] and you call Astro.insertDirective("img-src whatever"), what happens? Does it override, merge, or add (which is incorrect)?

Here are the 3 outcomes:

  • override: img-src whatever;. That is wrong IMO because you likely want to inherit the config from your astro config
  • add (current IIRC): img-src 'self'; img-src whatever;. I think this is wrong because only the last occurence will be picked up
  • merge (my recommendation): img-src 'self' whatever;. We preserve everything

What do you think?

@Eveeifyeve

This comment has been minimized.

@cleancommunities
Copy link

cleancommunities commented Aug 31, 2025

Hello, I was pointed in this direction from the discord, in relation to an inquiry about use cases of, "style-src-attr". Repo, https://github.com/cleancommunities/csp-test (https://discordapp.com/channels/830184174198718474/830184175176122389/1411782698720170046)

@ematipico
Copy link
Member Author

@florian-lefebvre

I would go for the third option, however I think we should change the APIs, the make the merging safer and less prone to error on our side e.g.

Astro.csp.insertDirective("img-src", "whatever")

@florian-lefebvre
Copy link
Member

Not necessarily, we already have the logic for it with font-src

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.