diff --git a/README.md b/README.md index bd6ebbc..046c975 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ app.listen(80, function () { ## Configuration Options * `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values: - - `Boolean` - set `origin` to `true` to reflect the [request origin](https://datatracker.ietf.org/doc/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS. + - `Boolean` - set `origin` to `true` to reflect the [request origin](https://datatracker.ietf.org/doc/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS. **Avoid combining `origin: true` with `credentials: true`** (see the `credentials` option below). - `String` - set `origin` to a specific origin. For example, if you set it to - `"http://example.com"` only requests from "http://example.com" will be allowed. - `"*"` for all domains to be allowed. @@ -230,6 +230,22 @@ app.listen(80, function () { * `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header. * `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed. * `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted. + + > **Security note:** Do not combine `credentials: true` with `origin: true`. When `origin` is `true`, this package reflects whatever origin the browser sends back in `Access-Control-Allow-Origin`. Paired with `Access-Control-Allow-Credentials: true`, this means *any* website can make credentialed (cookie-bearing) requests to your server and read the responses — equivalent to opening your API to the entire web with full session access. Use an explicit allowlist (`origin: ['https://app.example.com']`) instead, or a function for dynamic lists (env-specific, tenant-specific, etc.): + + ```js + app.use(cors({ + origin: function (origin, callback) { + const allowlist = process.env.ALLOWED_ORIGINS.split(','); + if (!origin || allowlist.includes(origin)) { + callback(null, true); + } else { + callback(new Error('Not allowed by CORS')); + } + }, + credentials: true + })); + ``` * `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted. * `preflightContinue`: Pass the CORS preflight response to the next handler. * `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.