nuxt-security icon indicating copy to clipboard operation
nuxt-security copied to clipboard

Setting `corsHandler.origin` issues

Open StrangeRanger opened this issue 1 year ago • 14 comments

Description

I encountered a similar issue as described in Issue 505 and Issue 497. However, I believe my case might be slightly different, so I’m opening a new issue.

When attempting to configure the corsHandler.origin to a specific value, such as https://hthompson.dev, the access-control-allow-origin header remains set to * instead of reflecting the specified origin. I have tried various configurations, but the issue persists. In Issue 505, the author mentioned the direct use of the access-control-allow-origin header, which I may have misunderstood, but applying that suggestion did not resolve the problem.

Ultimately, my goal is to configure the origin to something like ["https://hthompson.dev", "https://*.hthompson.dev"] or ["https://hthompson.dev", "https://analytics.hthompson.dev"]. But right now, I'm stumped.

Version

  • nuxt-security: 2.0.0-rc.9
  • nuxt: 3.12.4

Reproduction Link

Repository Link

Steps to Reproduce

  1. Clone the repository locally.
  2. Switch to the nuxt-security branch: git checkout nuxt-security.
  3. Install the dependencies and build the site: pnpm install && pnpm run build.
  4. Start the server: node .output/server/index.mjs.
  5. Perform a cURL request to retrieve the headers: curl -I http://localhost:3000.
  6. Observe that the access-control-allow-origin header remains set to *.

Expected Behavior

The access-control-allow-origin header should be set according to the configuration specified in the nuxt.config.ts file.

Actual Behavior

The header defaults to access-control-allow-origin: *, regardless of the specified configuration.

StrangeRanger avatar Aug 23 '24 20:08 StrangeRanger

Hi @StrangeRanger I am the author of #505.

Have you already tried the following? I was able to temporarily solve the problem by turning corsHandler off.

# nuxt.config.ts
# It worked.

export default {
    routeRules: {
            security: {
+                corsHandler: false // This temporarily solved the problem.
            },
            headers: {
                'Access-Control-Allow-Origin': ["https://hthompson.dev", "https://*.hthompson.dev"]
            }
        }
    },
}

However, it did not solve the root of the problem and I am still investigating the cause. šŸ¤”

myaaaapon avatar Sep 02 '24 08:09 myaaaapon

Sorry for not getting back to you sooner, @myaaaapon; I have not tried that. Though, with disabling the corsHandler, will that not affect the other security settings set by nuxt-security?

As another note, I don't have routeRules in my nuxt.config.ts file. Would I just do the following:

export default defineNuxtConfig({
    ........................,
    routeRules: {
        headers: {
            'Access-Control-Allow-Origin': ["https://hthompson.dev/", "https://*.hthompson.dev"]
        }
    },
    security: {
		corsHandler: false,
        ...........
    },
    ............
});

StrangeRanger avatar Sep 03 '24 15:09 StrangeRanger

Sorry for not getting back to you sooner, @myaaaapon; I have not tried that. Though, with disabling the corsHandler, will that not affect the other security settings set by nuxt-security?

As another note, I don't have routeRules in my nuxt.config.ts file. Would I just do the following:

export default defineNuxtConfig({
    ........................,
    routeRules: {
        headers: {
            'Access-Control-Allow-Origin': ["https://hthompson.dev/", "https://*.hthompson.dev"]
        }
    },
    security: {
		corsHandler: false,
        ...........
    },
    ............
});

Thank you StrangeRanger to open this issue, actually I have facing with this few days ago and struck on this. After try setting the cors in routeRules -> it worked on windows browser and android. But I wondering have you tried on ios browser?

I always get 403 once request api except change the 'Access-Control-Allow-Origin' to '*'. Need something help.

whitersun avatar Sep 24 '24 16:09 whitersun

Having the same issue here, would be nice if this can get fixed.

jschroeter avatar Oct 24 '24 17:10 jschroeter

@Morgbn would you be able to help here? :)

Baroshem avatar Oct 25 '24 09:10 Baroshem

Hello, don't know how I can help, the reproduction link is down. But note that https://*.hthompson.dev must be a regex. Also, I have a similar setup that works, maybe it can help :

routeRules: {
    '/api/contact/': {
      csurf: false,
      security: {
        enabled: true,
        corsHandler: {
          origin: process.env.CONTACT_ORIGIN, // for example https://hthompson.dev
          methods: ['POST', 'OPTIONS']
        }
      }
    }
  }

if I do:

curl 'http://localhost:3300/api/contact' \
  -H 'accept: application/json' \
  -H 'content-type: application/json' \
  -H 'Origin: https://hthompson.dev' \
  --data-raw '{}' \
  --verbose

I have < access-control-allow-origin: https://hthompson.dev

Useful links: h3 handleCors function calling appendCorsHeaders calling createOriginHeaders calling isCorsOriginAllowed

Morgbn avatar Nov 01 '24 13:11 Morgbn

Thanks for the help @Morgbn . You are a 🌟

Baroshem avatar Nov 04 '24 08:11 Baroshem

@whitersun Thank you for your reply. Your workaround works for me only if I specify the route (i.e., '/*'), which I realized from @Morgbn's example. I at least have a temporary solution, thanks to you both; I appreciate it.

@Morgbn Unfortunately, your method didn't work for me. As for your curl command, the output displays < access-control-allow-origin: https://hthompson.dev even if I don't apply your suggested configurations. I found this resulted from specifying -H 'Origin: https://hthompson.dev' \, and while that's great, it doesn't accurately reflect what happens when accessing my website from a browser. Though when I applied the workaround provided by @whitersun and execute the curl command, with and without the -H 'Origin: https://hthompson.dev' \ option, I now get < access-control-allow-origin: https://hthompson.dev, https://*.hthompson.dev.

@Baroshem, I just wanted to ping you in this response in case it provided any helpful information. I've also provided my current nuxt.config.ts file below.

nuxt.config.ts:

// https://nuxt.com/docs/api/configuration/nuxt-config
import vuetify, { transformAssetUrls } from "vite-plugin-vuetify";

export default defineNuxtConfig({
  plugins: [
    process.env.NODE_ENV !== "development"
      ? "plugins/production/vue-matomo.client.js"
      : "",
    process.env.NODE_ENV !== "development"
      ? "plugins/production/cloudflare.client.js"
      : "",
  ].filter(Boolean),

  devtools: { enabled: true },

  build: {
    transpile: ["vuetify"],
  },

  modules: [
    "@nuxt/eslint",
    "nuxt-security",
    "@nuxt/devtools",
    (_options, nuxt) => {
      nuxt.hooks.hook("vite:extendConfig", (config) => {
        config.plugins.push(vuetify({ autoImport: true }));
      });
    },
  ],

  routeRules: {
    "/*": {
      headers: {
        "Access-Control-Allow-Origin": [
          "https://hthompson.dev",
          "https://*.hthompson.dev",
        ],
      },
    },
  },

  security: {
    enabled: true,
    strict: true,
    nonce: true,
    corsHandler: false,
    //corsHandler: {
    //  origin: ["https://hthompson.dev", "https://*.hthompson.dev"],
    //},
    allowedMethodsRestricter: {
      methods: ["GET", "HEAD", "OPTIONS"],
    },
    headers: {
      crossOriginEmbedderPolicy:
        process.env.NODE_ENV === "development" ? "unsafe-none" : "require-corp",
      contentSecurityPolicy: {
        "default-src": ["'self'"],
        "img-src": ["'self'", "blob:"],
        "style-src": ["'self'", "https:", "'unsafe-inline'"],
        "connect-src": ["'self'", "https://analytics.hthompson.dev"],
        "script-src": [
          "'self'",
          "https:",
          "'unsafe-inline'",
          "'strict-dynamic'",
          "'nonce-{{nonce}}'",
          "https://analytics.hthompson.dev",
          "https://files.hthompson.dev/scripts/tracking.js",
          "https://static.cloudflareinsights.com",
        ],
      },
      referrerPolicy: "same-origin",
      strictTransportSecurity: {
        maxAge: 31536000,
        includeSubdomains: true,
        preload: true,
      },
      xContentTypeOptions: "nosniff",
      xFrameOptions: "SAMEORIGIN",
      xXSSProtection: "1; mode=block",
    },
    hidePoweredBy: true,
  },

  vite: {
    vue: {
      template: {
        transformAssetUrls,
      },
    },
  },

  css: ["~/assets/css/main.css"],
  telemetry: false,
  compatibilityDate: "2024-10-19",
});

Here is a link to easily display the headers of the current version of my website to see it in action: https://securityheaders.com/?q=hthompson.dev&followRedirects=on

StrangeRanger avatar Nov 20 '24 22:11 StrangeRanger

Adding to @StrangeRanger answer, /* adds access-control-allow-origin to all base level routes. In case you have nested routes, you need to add /*/*... depending on level of nesting you have in your directory.

Aniket-508 avatar Dec 25 '24 15:12 Aniket-508

Same here this looks like a bug

Hronom avatar Sep 15 '25 21:09 Hronom

Could it be an upstream issue with H3?

Baroshem avatar Sep 16 '25 05:09 Baroshem

I don't know, but the fact is there strange behavior where access-control-allow-origin not respects what you have in config.

Expectation is to set nuxt-security and get CORS support in all endpoints including Nuxt server side /api. If /api endpoints not supported - then it should be clearly mentioned here https://nuxt-security.vercel.app/middleware/cors-handler The workaround with corsHandler disabling not an option.

Not sure why maintainers ignore this issues, it's pretty critical bug.

Hronom avatar Sep 16 '25 20:09 Hronom

Hey @Hronom

Please remember that this is an Open Source project so contributions are always welcome. If you have an idea how to solve it, feel free to create a Pull Request and I will be happy to review it and merge it to unblock you :)

Baroshem avatar Sep 16 '25 20:09 Baroshem

@Baroshem ok, so I created simple project to check issue https://github.com/Hronom/nuxt-security-playground/tree/main CSRF disabled.

When I send:

curl --location --request POST 'http://localhost:3000/api/test' \
--header 'Origin: http://superfish:3000' \
--header 'Cookie: csrf=3e4e9237-a674-4b2a-9a02-1620863d83f6'

Response:

* Host localhost:3000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:3000...
* Connected to localhost (::1) port 3000
> POST /api/test HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/8.5.0
> Accept: */*
> Origin: http://superfish:3000
> Cookie: csrf=3e4e9237-a674-4b2a-9a02-1620863d83f6
> 
< HTTP/1.1 200 OK
< 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: http://superfish:3000
< vary: origin
< access-control-allow-credentials: true
< content-type: application/json
< date: Wed, 17 Sep 2025 00:50:47 GMT
< connection: close
< content-length: 30
< 
{
  "status": "ok from post"
* Closing connection
}

Looks like ok.

But when I use cURL:

curl --location --request POST 'http://localhost:3000/api/test' \
--header 'Cookie: csrf=3e4e9237-a674-4b2a-9a02-1620863d83f6' -v

Response:

* Host localhost:3000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:3000...
* Connected to localhost (::1) port 3000
> POST /api/test HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< 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: *
< access-control-allow-credentials: true
< content-type: application/json
< date: Wed, 17 Sep 2025 00:49:27 GMT
< connection: close
< content-length: 30
< 
{
  "status": "ok from post"
* Closing connection
}

My expectation was to not have access-control-allow-origin: *(more clean implementation) for when origin is not added(e.g. not a CORS request).

Like for example in this cURL:

curl --location --request POST 'http://localhost:3000/api/test' \
--header 'Origin: http://localhost:3000' \
--header 'Cookie: csrf=3e4e9237-a674-4b2a-9a02-1620863d83f6'

Response

* Host localhost:3000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:3000...
* Connected to localhost (::1) port 3000
> POST /api/test HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/8.5.0
> Accept: */*
> Origin: http://localhost:3000
> Cookie: csrf=3e4e9237-a674-4b2a-9a02-1620863d83f6
> 
< HTTP/1.1 200 OK
< 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-credentials: true
< content-type: application/json
< date: Wed, 17 Sep 2025 01:04:23 GMT
< connection: close
< content-length: 30
< 
{
  "status": "ok from post"
* Closing connection
}

No access-control-allow-origin header at all.

I guess it's fine in terms of security, but maybe confusing as people expect to see access-control-allow-origin: http://superfish:3000 e.g. what was set in corsHandler.origin

Hronom avatar Sep 17 '25 00:09 Hronom