nuxt-modules / 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
830 stars 60 forks source link

X-Content-Type-Options nosniff duplicated ? #575

Open GreyXor opened 2 days ago

GreyXor commented 2 days ago

Version

nuxt-security: 2.1.4 nuxt: 3.14.1592

Image

Image

Hello, I build an app with pnpm build --preset=cloudflare-pages-static and I see some duplicates entries in _headers. then that cause cloudflare to send in double some heaeders, that's normal ?

vejja commented 2 days ago

Not normal Do you have duplicates in dev mode?

GreyXor commented 2 days ago

After I ran pnpm build --preset=cloudflare-pages-static. I have this dist/_headers

/_nuxt/builds/meta/*
  cache-control: public, max-age=31536000, immutable
/_nuxt/builds/*
  cache-control: public, max-age=1, immutable
/sitemap.xsl
  Content-Type: application/xslt+xml
/_nuxt/*
  cache-control: public, max-age=31536000, immutable
/200.html
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  x-content-type-options: nosniff
  x-download-options: noopen
  x-frame-options: DENY
  x-permitted-cross-domain-policies: none
  x-xss-protection: 0
  access-control-allow-origin: *
  x-robots-tag: index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1
  cross-origin-resource-policy: same-origin
  cross-origin-opener-policy: same-origin
  cross-origin-embedder-policy: require-corp
  content-security-policy: base-uri 'none'; default-src 'none'; connect-src *; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src-attr 'none'; style-src 'self' 'sha384-f03nx78rOCnt6AwELJMV23DoKZkgSc38OV+pGbyjAGJ5fp6iw4cJnouUWcAp0YoL'; script-src 'self' 'strict-dynamic' 'sha256-gA1UKgUEtDt2cidaUsoCZuF2sWpGSFUt2uXOBWMbCZM=' 'sha256-fS31OOiK/d40YbVTuBvQgFn10Npo1aV7bXp3h5YkeKI=' 'sha256-XMTtN7sjHppnSdg9yFi3iIW2ZYe401Hkaw9C4u4RTO8=' 'sha384-O1ypfnqBHcffeqgUF8m9Qzicp5gzsENWl6MuXGu0fUtLvrEAnF/aB8FCV2BG4XHx'; upgrade-insecure-requests; worker-src 'self';
  origin-agent-cluster: ?1
  x-dns-prefetch-control: off
  permissions-policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=()
  content-type: text/html;charset=utf-8
/404.html
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  x-content-type-options: nosniff
  x-download-options: noopen
  x-frame-options: DENY
  x-permitted-cross-domain-policies: none
  x-xss-protection: 0
  access-control-allow-origin: *
  x-robots-tag: index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1
  cross-origin-resource-policy: same-origin
  cross-origin-opener-policy: same-origin
  cross-origin-embedder-policy: require-corp
  content-security-policy: base-uri 'none'; default-src 'none'; connect-src *; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src-attr 'none'; style-src 'self' 'sha384-f03nx78rOCnt6AwELJMV23DoKZkgSc38OV+pGbyjAGJ5fp6iw4cJnouUWcAp0YoL'; script-src 'self' 'strict-dynamic' 'sha256-gA1UKgUEtDt2cidaUsoCZuF2sWpGSFUt2uXOBWMbCZM=' 'sha256-fS31OOiK/d40YbVTuBvQgFn10Npo1aV7bXp3h5YkeKI=' 'sha256-XMTtN7sjHppnSdg9yFi3iIW2ZYe401Hkaw9C4u4RTO8=' 'sha384-O1ypfnqBHcffeqgUF8m9Qzicp5gzsENWl6MuXGu0fUtLvrEAnF/aB8FCV2BG4XHx'; upgrade-insecure-requests; worker-src 'self';
  origin-agent-cluster: ?1
  x-dns-prefetch-control: off
  permissions-policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=()
  content-type: text/html;charset=utf-8
/
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  x-content-type-options: nosniff
  x-download-options: noopen
  x-frame-options: DENY
  x-permitted-cross-domain-policies: none
  x-xss-protection: 0
  access-control-allow-origin: *
  x-robots-tag: index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1
  cross-origin-resource-policy: same-origin
  cross-origin-opener-policy: same-origin
  cross-origin-embedder-policy: require-corp
  content-security-policy: base-uri 'none'; default-src 'none'; connect-src *; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src-attr 'none'; style-src 'self' 'sha256-UdfdXjfVHznHxwWlHuNuP27rkN3lBKcVnhZogpI5r78=' 'sha384-f03nx78rOCnt6AwELJMV23DoKZkgSc38OV+pGbyjAGJ5fp6iw4cJnouUWcAp0YoL'; script-src 'self' 'strict-dynamic' 'sha256-gKRuwZ+smqCIe8ekh9BSEjP/JMiE/HydqXdklskRuSI=' 'sha256-r0Kb+H/sw2Iz9Eook98TRhCAPotgSM7Rt3swxyFA1ck=' 'sha256-XMTtN7sjHppnSdg9yFi3iIW2ZYe401Hkaw9C4u4RTO8=' 'sha384-O1ypfnqBHcffeqgUF8m9Qzicp5gzsENWl6MuXGu0fUtLvrEAnF/aB8FCV2BG4XHx' 'sha384-ylPEr5j/hF/bSw37JlKOi7oibm+rTmVGYTTY86WeIkoyWvuminVkdYb6xtGp7Otk' 'sha384-ymbnSmVFGtrsjQDRc78Nk9nQGIFZf8k8GW0Z+IOnftyCqCN8yLM9bXUAIJEE2z0f' 'sha384-CLIB45fgjg9w1i3EOkUyatVJt21CdR0wzjmJnCuy2wYWk5fDZ7CuPyMc3zMo1PpZ' 'sha384-woIiDj1p8pe5KDYJD5Zhlr3JDN/xpRuDybcKtC+JQRQ48vOCCwV07CjjLH6b8Vx7' 'sha384-GmdH5Y3CkHFK2DwR6PFvawjSdL99LqW6B/dNrFdM6EDbKp7tUXBX+iunzp8HWipz' 'sha384-G1G4K9Cdpsm+OUxwyNecEZXUnk2CkUz270l7cwQaL57caava7SVXtzHzBSd3UInU' 'sha384-e6dRA9EojU+KQVAfHQAg6NuNuaZiVe4cQArItDF+7yfEbmAvC16+nqedrebVDzFL'; upgrade-insecure-requests; worker-src 'self';
  origin-agent-cluster: ?1
  x-dns-prefetch-control: off
  permissions-policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=()
  content-type: text/html;charset=utf-8
/server
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  x-content-type-options: nosniff
  x-download-options: noopen
  x-frame-options: DENY
  x-permitted-cross-domain-policies: none
  x-xss-protection: 0
  access-control-allow-origin: *
  x-robots-tag: index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1
  cross-origin-resource-policy: same-origin
  cross-origin-opener-policy: same-origin
  cross-origin-embedder-policy: require-corp
  content-security-policy: base-uri 'none'; default-src 'none'; connect-src *; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src-attr 'none'; style-src 'self' 'sha256-UdfdXjfVHznHxwWlHuNuP27rkN3lBKcVnhZogpI5r78=' 'sha384-f03nx78rOCnt6AwELJMV23DoKZkgSc38OV+pGbyjAGJ5fp6iw4cJnouUWcAp0YoL'; script-src 'self' 'strict-dynamic' 'sha256-df8dhj665lYudfXbvs9oaGlTADm07l26HhpaEHl/jq8=' 'sha256-q1m60j+ht3Y9a0Ev9eFUHO83P7ksur8gevJGx6yIkuM=' 'sha256-XMTtN7sjHppnSdg9yFi3iIW2ZYe401Hkaw9C4u4RTO8=' 'sha384-O1ypfnqBHcffeqgUF8m9Qzicp5gzsENWl6MuXGu0fUtLvrEAnF/aB8FCV2BG4XHx' 'sha384-ylPEr5j/hF/bSw37JlKOi7oibm+rTmVGYTTY86WeIkoyWvuminVkdYb6xtGp7Otk' 'sha384-ymbnSmVFGtrsjQDRc78Nk9nQGIFZf8k8GW0Z+IOnftyCqCN8yLM9bXUAIJEE2z0f' 'sha384-CLIB45fgjg9w1i3EOkUyatVJt21CdR0wzjmJnCuy2wYWk5fDZ7CuPyMc3zMo1PpZ' 'sha384-woIiDj1p8pe5KDYJD5Zhlr3JDN/xpRuDybcKtC+JQRQ48vOCCwV07CjjLH6b8Vx7' 'sha384-GmdH5Y3CkHFK2DwR6PFvawjSdL99LqW6B/dNrFdM6EDbKp7tUXBX+iunzp8HWipz' 'sha384-G1G4K9Cdpsm+OUxwyNecEZXUnk2CkUz270l7cwQaL57caava7SVXtzHzBSd3UInU' 'sha384-e6dRA9EojU+KQVAfHQAg6NuNuaZiVe4cQArItDF+7yfEbmAvC16+nqedrebVDzFL' 'sha384-tJd1zSyyVGXwcklmH1jwMt/dBgC4cBkDaBCdfqu8PRYERQ/IWi/UWD+3qucS+FvB'; upgrade-insecure-requests; worker-src 'self';
  origin-agent-cluster: ?1
  x-dns-prefetch-control: off
  permissions-policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=()
  content-type: text/html;charset=utf-8
/console
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  x-content-type-options: nosniff
  x-download-options: noopen
  x-frame-options: DENY
  x-permitted-cross-domain-policies: none
  x-xss-protection: 0
  access-control-allow-origin: *
  x-robots-tag: index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1
  cross-origin-resource-policy: same-origin
  cross-origin-opener-policy: same-origin
  cross-origin-embedder-policy: require-corp
  content-security-policy: base-uri 'none'; default-src 'none'; connect-src *; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src-attr 'none'; style-src 'self' 'sha256-UdfdXjfVHznHxwWlHuNuP27rkN3lBKcVnhZogpI5r78=' 'sha384-f03nx78rOCnt6AwELJMV23DoKZkgSc38OV+pGbyjAGJ5fp6iw4cJnouUWcAp0YoL' 'sha384-2FDO4EtrxO6iDmbhfnnT4LRzGhPIFREAKN4R3qLSNLHgqMf4lh1jqOMg9Hd1HQY7'; script-src 'self' 'strict-dynamic' 'sha256-Uafai4qtsVNcTkdgxrioFMGt5473HSoofwMgC6vOqEA=' 'sha256-80vwQVcHFwAzDTCPEKTLDViuIO6khm8ynHpK/GwXuJQ=' 'sha256-XMTtN7sjHppnSdg9yFi3iIW2ZYe401Hkaw9C4u4RTO8=' 'sha384-O1ypfnqBHcffeqgUF8m9Qzicp5gzsENWl6MuXGu0fUtLvrEAnF/aB8FCV2BG4XHx' 'sha384-ylPEr5j/hF/bSw37JlKOi7oibm+rTmVGYTTY86WeIkoyWvuminVkdYb6xtGp7Otk' 'sha384-ymbnSmVFGtrsjQDRc78Nk9nQGIFZf8k8GW0Z+IOnftyCqCN8yLM9bXUAIJEE2z0f' 'sha384-CLIB45fgjg9w1i3EOkUyatVJt21CdR0wzjmJnCuy2wYWk5fDZ7CuPyMc3zMo1PpZ' 'sha384-woIiDj1p8pe5KDYJD5Zhlr3JDN/xpRuDybcKtC+JQRQ48vOCCwV07CjjLH6b8Vx7' 'sha384-GmdH5Y3CkHFK2DwR6PFvawjSdL99LqW6B/dNrFdM6EDbKp7tUXBX+iunzp8HWipz' 'sha384-G1G4K9Cdpsm+OUxwyNecEZXUnk2CkUz270l7cwQaL57caava7SVXtzHzBSd3UInU' 'sha384-e6dRA9EojU+KQVAfHQAg6NuNuaZiVe4cQArItDF+7yfEbmAvC16+nqedrebVDzFL' 'sha384-J/uFW5rsYYVLm53X0OebMECk5/seFHGWJzh367z3hFM+5YSc/ChXXIvMuAZ56CC+'; upgrade-insecure-requests; worker-src 'self';
  origin-agent-cluster: ?1
  x-dns-prefetch-control: off
  permissions-policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=()
  content-type: text/html;charset=utf-8
/settings
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  x-content-type-options: nosniff
  x-download-options: noopen
  x-frame-options: DENY
  x-permitted-cross-domain-policies: none
  x-xss-protection: 0
  access-control-allow-origin: *
  x-robots-tag: index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1
  cross-origin-resource-policy: same-origin
  cross-origin-opener-policy: same-origin
  cross-origin-embedder-policy: require-corp
  content-security-policy: base-uri 'none'; default-src 'none'; connect-src *; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src-attr 'none'; style-src 'self' 'sha256-UdfdXjfVHznHxwWlHuNuP27rkN3lBKcVnhZogpI5r78=' 'sha384-f03nx78rOCnt6AwELJMV23DoKZkgSc38OV+pGbyjAGJ5fp6iw4cJnouUWcAp0YoL'; script-src 'self' 'strict-dynamic' 'sha256-peq6HdqwADViZy0yHttmVgM3kRT8opnE1Ns7gRzdQJM=' 'sha256-3tFygGBwVe8Pg5A7aNF360JW1nRDyGyectc0kc57p8g=' 'sha256-XMTtN7sjHppnSdg9yFi3iIW2ZYe401Hkaw9C4u4RTO8=' 'sha384-O1ypfnqBHcffeqgUF8m9Qzicp5gzsENWl6MuXGu0fUtLvrEAnF/aB8FCV2BG4XHx' 'sha384-ylPEr5j/hF/bSw37JlKOi7oibm+rTmVGYTTY86WeIkoyWvuminVkdYb6xtGp7Otk' 'sha384-ymbnSmVFGtrsjQDRc78Nk9nQGIFZf8k8GW0Z+IOnftyCqCN8yLM9bXUAIJEE2z0f' 'sha384-CLIB45fgjg9w1i3EOkUyatVJt21CdR0wzjmJnCuy2wYWk5fDZ7CuPyMc3zMo1PpZ' 'sha384-woIiDj1p8pe5KDYJD5Zhlr3JDN/xpRuDybcKtC+JQRQ48vOCCwV07CjjLH6b8Vx7' 'sha384-GmdH5Y3CkHFK2DwR6PFvawjSdL99LqW6B/dNrFdM6EDbKp7tUXBX+iunzp8HWipz' 'sha384-G1G4K9Cdpsm+OUxwyNecEZXUnk2CkUz270l7cwQaL57caava7SVXtzHzBSd3UInU' 'sha384-e6dRA9EojU+KQVAfHQAg6NuNuaZiVe4cQArItDF+7yfEbmAvC16+nqedrebVDzFL' 'sha384-tJd1zSyyVGXwcklmH1jwMt/dBgC4cBkDaBCdfqu8PRYERQ/IWi/UWD+3qucS+FvB'; upgrade-insecure-requests; worker-src 'self';
  origin-agent-cluster: ?1
  x-dns-prefetch-control: off
  permissions-policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=()
  content-type: text/html;charset=utf-8
/*
  Referrer-Policy: no-referrer
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload;
  X-Content-Type-Options: nosniff
  X-Download-Options: noopen
  X-Frame-Options: DENY
  X-Permitted-Cross-Domain-Policies: none
  X-XSS-Protection: 0

no. Devmode don't duplicate entries

vejja commented 2 days ago

Which headers are duplicated? Can't see them above

GreyXor commented 2 days ago
/settings
  x-content-type-options: nosniff

and

/*
  X-Content-Type-Options: nosniff

Also one is uppercase and one other is not thanks!

vejja commented 2 days ago

ok got it the route definitions seem correct but it looks like cloudflare creates the duplicates when it merges /* with /settings is that normal?

GreyXor commented 8 hours ago

ok got it the route definitions seem correct but it looks like cloudflare creates the duplicates when it merges /* with /settings is that normal?

If in dev mode it's working as expected. that's mean it's a cloudflare issue ? I think so

vejja commented 8 hours ago

I looked at the Cloudflare docs here : https://developers.cloudflare.com/pages/configuration/headers/ They have this section: Image

This is why the headers are getting duplicated

@pi0 as per you suggestion to track https://github.com/nitrojs/nitro/issues/2909 here: I'm wondering how Nitro can handle this. I think the issue with the headers is that each vendor has different priority rules:

Maybe we can start with the Nuxt approach: What is the assumption that routeRules makes for multiple matches? For clarity let's say that we have the following route rules:

routeRules: {
  '/**': {
    headers: {
      foo: 'bar'
    },
  '/settings': {
    headers: {
      foo: 'baz'
    }
  }
}

Am I correct to assume the /settings page will return header Foo: baz when delivered in SSR by Nitro ?

See Stackblitz here : https://stackblitz.com/~/github.com/vejja/nuxt-starter-uimpbg