Baroshem / nuxt-security

πŸ›‘ Automatically configure your app to follow OWASP security patterns and principles by using HTTP Headers and Middleware
https://nuxt-security.vercel.app/
MIT License
815 stars 56 forks source link

Too many header rules - Cloudflare Pages #504

Open Vahagn-Zaqaryan opened 3 months ago

Vahagn-Zaqaryan commented 3 months ago

Hi there,

I'm new to the nuxt-security module and started using it just yesterday. I have a blog with over 1,000 articles, and I use SSG to statically generate the pages, which I then upload to Cloudflare Pages for hosting. Initially, I was testing the configuration in my development environment without any issues. However, upon switching to production, I noticed that the security headers were not being applied.

After checking the logs, I discovered that Cloudflare Pages has a limit of 100 headers, as mentioned on their Limits page.

Here is the error message I encountered: Error

I currently have about 1,300 header rules, which exceeds the limit.

What can I do to fix this issue?

Baroshem commented 3 months ago

Hey there, are you using the http meta equiv for CSP or do you use SSR to send generated response headers?

I honestly don't have that much experience with Cloudflare Pages nor the headers limit.

Maybe @pi0 would be able to share some light on it? :)

vejja commented 3 months ago

Hi @Vahagn-Zaqaryan You can use ssg.exportToPresets : false. This will prevent writing header rules into the Cloudflare preset. The downside is that CSP will not be transmitted via headers, but only via http equiv meta tag. The issue here is that you have 1,000 static pages, so we generate 1,000 different headers rules and this is beyond Cloudflare's limit.

Maybe a better alternative, if you want to keep CSP in headers, is to use the nuxt-security:prerenderedHeaders hook and generate your header rules yourself. I suspect there are not 1,000 different hashes, so probably by inspecting the CSP you will find a way to regroup the policies in a much lower number of rules. I'm happy to assist you with that task if you give me some details. We could benefit from your use case to improve the way the module generates the rules.

Baroshem commented 2 months ago

@Vahagn-Zaqaryan have you tried the recommendation from @vejja ? :)

vejja commented 2 months ago

@Vahagn-Zaqaryan have you tried the recommendation from @vejja ? :)

+1 I would be extremely interested in inspecting your rules. The way we generate rules is pretty dumb right now, as evidenced in your Cloudflare logs it’s a file with 21,470 lines… I’m pretty sure we can largely improve this

Vahagn-Zaqaryan commented 2 months ago

Hey @Baroshem and @vejja,

I apologize for the delayed response; I've been caught up with some releases! πŸŽ‰

I’d like to provide some additional context that might help. My current project is a Nuxt 3 application integrated with a Diectus headless CMS. The route structure mirrors the various content types I’m managing, as outlined below:

src
β”œβ”€β”€ services
β”‚   └── [id]
β”‚       └── [type]
β”œβ”€β”€ categories
β”‚   └── [id]
β”œβ”€β”€ docs
β”‚   └── [slug]
β”œβ”€β”€ articles
β”‚   └── [slug]
β”œβ”€β”€ news
β”‚   └── [slug]
β”œβ”€β”€ [slug]   # Custom pages
β”œβ”€β”€ search
└── index

I used the method @vejja suggested and it worked! However, it appears that http-equiv only works for specific headers, which might limit its effectiveness as a comprehensive solution.

For reference, here is my current nuxt-security configuration:

security: {
  nonce: true,
  headers: {
    xFrameOptions: 'ALLOWALL',
    permissionsPolicy:
      'accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()',
    contentSecurityPolicy: {
      'img-src': ["'self'"],
      'script-src': ["'self'", "'nonce-{{nonce}}'", process.env.NUXT_PUBLIC_APP_URL, 'https://*.cloudflare.com'],
    },
  },
},

Additionally, I’ve taken a closer look at my _headers file this is a part of it. The differences are primarily in the script hashes, especially the first hash, while the remaining headers are the same.

I tried to find the scripts that are causing the discrepancies in the generated headers, but I couldn't, It’s possible these are inline scripts or styles with hashes calculated dynamically during browser execution.

Please let me know if there’s anything else I can provide to help you guys improve the module πŸš€

Baroshem commented 1 month ago

@vejja thanks for your great tip. Can we get anything from @Vahagn-Zaqaryan comments and improve the module based on it?

Vahagn-Zaqaryan commented 1 month ago

@Baroshem and @vejja Thanks a lot for your support! Happy to help make the module better πŸš€

For others who might find this issue... I switched to SSR using the Cloudflare Pages but this affected the load time of my website in any case, I'm eagerly waiting for the fixes to be released!

Baroshem commented 1 month ago

@vejja when you have time :)

vejja commented 1 month ago

Hi guys, I'd love to find a solution for this one but it doesn't seem like there is an obvious solution.

@Vahagn-Zaqaryan I looked at your _headers file, and there is no common set between the routes that you extracted. I suppose that when you looked at the subfolders and sub-subfolders, there was no common pattern either. So in your case, the headers would be unique for each of the 1,000 pages - which at the end of the day means that we cannot reduce the number of header rules.

With hindsight I think the solution would be to introduce a new value for ssg.exportToPresets such as 'allExceptCSP' where only CSP would be excluded from headers and instead transmitted via http-equiv. This will in any case be complex to implement as we will need some kind of AST resolution algorithm to merge down all other headers into the least common denominator. Any ideas welcome @Baroshem

Baroshem commented 1 month ago

Thanks for your insight Sebastien.

Looking at the description I think this is quite a lot of work for an edge case so I would recommend not implementing it. Every new code added to the module needs to be maintained by us and maintaining a big and complex code that is used to solve an edge case is not worth for me.

I am up for your comments here as well. Please let me know :)

vejja commented 1 month ago

We might not need an AST parser after all Let me think about it again