Skip to content

Commit 53661ea

Browse files
authored
fix(cors): avoid overwriting Access-Control-Expose-Headers response header (#11136)
1 parent e40a9d1 commit 53661ea

File tree

6 files changed

+125
-17
lines changed

6 files changed

+125
-17
lines changed

apisix/plugins/cors.lua

+6-6
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,9 @@ local schema = {
7575
},
7676
expose_headers = {
7777
description =
78-
"you can use '*' to expose all header when no credentials," ..
79-
"'**' to allow forcefully(it will bring some security risks, be carefully)," ..
80-
"multiple header use ',' to split. default: *.",
81-
type = "string",
82-
default = "*"
78+
"multiple header use ',' to split." ..
79+
"If not specified, no custom headers are exposed.",
80+
type = "string"
8381
},
8482
max_age = {
8583
description =
@@ -226,7 +224,9 @@ local function set_cors_headers(conf, ctx)
226224
core.response.set_header("Access-Control-Allow-Origin", ctx.cors_allow_origins)
227225
core.response.set_header("Access-Control-Allow-Methods", allow_methods)
228226
core.response.set_header("Access-Control-Max-Age", conf.max_age)
229-
core.response.set_header("Access-Control-Expose-Headers", conf.expose_headers)
227+
if conf.expose_headers ~= nil and conf.expose_headers ~= "" then
228+
core.response.set_header("Access-Control-Expose-Headers", conf.expose_headers)
229+
end
230230
if conf.allow_headers == "**" then
231231
core.response.set_header("Access-Control-Allow-Headers",
232232
core.request.header(ctx, "Access-Control-Request-Headers"))

docs/en/latest/plugins/cors.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The `cors` Plugins lets you enable [CORS](https://developer.mozilla.org/en-US/do
3939
| allow_origins | string | False | "*" | Origins to allow CORS. Use the `scheme://host:port` format. For example, `https://somedomain.com:8081`. If you have multiple origins, use a `,` to list them. If `allow_credential` is set to `false`, you can enable CORS for all origins by using `*`. If `allow_credential` is set to `true`, you can forcefully allow CORS on all origins by using `**` but it will pose some security issues. |
4040
| allow_methods | string | False | "*" | Request methods to enable CORS on. For example `GET`, `POST`. Use `,` to add multiple methods. If `allow_credential` is set to `false`, you can enable CORS for all methods by using `*`. If `allow_credential` is set to `true`, you can forcefully allow CORS on all methods by using `**` but it will pose some security issues. |
4141
| allow_headers | string | False | "*" | Headers in the request allowed when accessing a cross-origin resource. Use `,` to add multiple headers. If `allow_credential` is set to `false`, you can enable CORS for all request headers by using `*`. If `allow_credential` is set to `true`, you can forcefully allow CORS on all request headers by using `**` but it will pose some security issues. |
42-
| expose_headers | string | False | "*" | Headers in the response allowed when accessing a cross-origin resource. Use `,` to add multiple headers. If `allow_credential` is set to `false`, you can enable CORS for all response headers by using `*`. If `allow_credential` is set to `true`, you can forcefully allow CORS on all response headers by using `**` but it will pose some security issues. |
42+
| expose_headers | string | False | | Headers in the response allowed when accessing a cross-origin resource. Use `,` to add multiple headers. If `allow_credential` is set to `false`, you can enable CORS for all response headers by using `*`. If not specified, the plugin will not modify the `Access-Control-Expose-Headers header`. See [Access-Control-Expose-Headers - MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) for more details. |
4343
| max_age | integer | False | 5 | Maximum time in seconds the result is cached. If the time is within this limit, the browser will check the cached result. Set to `-1` to disable caching. Note that the maximum value is browser dependent. See [Access-Control-Max-Age](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age#Directives) for more details. |
4444
| allow_credential | boolean | False | false | When set to `true`, allows requests to include credentials like cookies. According to CORS specification, if you set this to `true`, you cannot use '*' to allow all for the other attributes. |
4545
| allow_origins_by_regex | array | False | nil | Regex to match origins that allow CORS. For example, `[".*\.test.com$"]` can match all subdomains of `test.com`. When set to specified range, only domains in this range will be allowed, no matter what `allow_origins` is. |
@@ -119,7 +119,6 @@ curl http://127.0.0.1:9080/hello -v
119119
< Access-Control-Allow-Origin: *
120120
< Access-Control-Allow-Methods: *
121121
< Access-Control-Allow-Headers: *
122-
< Access-Control-Expose-Headers: *
123122
< Access-Control-Max-Age: 5
124123
...
125124
```

docs/zh/latest/plugins/cors.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ description: 本文介绍了 Apache APISIX cors 插件的基本信息及使用
3737
| allow_origins | string || "*" | 允许跨域访问的 Origin,格式为 `scheme://host:port`,示例如 `https://somedomain.com:8081`。如果你有多个 Origin,请使用 `,` 分隔。当 `allow_credential``false` 时,可以使用 `*` 来表示允许所有 Origin 通过。你也可以在启用了 `allow_credential` 后使用 `**` 强制允许所有 Origin 均通过,但请注意这样存在安全隐患。 |
3838
| allow_methods | string || "*" | 允许跨域访问的 Method,比如:`GET``POST` 等。如果你有多个 Method,请使用 `,` 分割。当 `allow_credential``false` 时,可以使用 `*` 来表示允许所有 Method 通过。你也可以在启用了 `allow_credential` 后使用 `**` 强制允许所有 Method 都通过,但请注意这样存在安全隐患。 |
3939
| allow_headers | string || "*" | 允许跨域访问时请求方携带哪些非 `CORS 规范` 以外的 Header。如果你有多个 Header,请使用 `,` 分割。当 `allow_credential``false` 时,可以使用 `*` 来表示允许所有 Header 通过。你也可以在启用了 `allow_credential` 后使用 `**` 强制允许所有 Header 都通过,但请注意这样存在安全隐患。 |
40-
| expose_headers | string || "*" | 允许跨域访问时响应方携带哪些非 `CORS 规范` 以外的 Header。如果你有多个 Header,请使用 `,` 分割。当 `allow_credential``false` 时,可以使用 `*` 来表示允许任意 Header。你也可以在启用了 `allow_credential` 后使用 `**` 强制允许任意 Header,但请注意这样存在安全隐患|
40+
| expose_headers | string || | 允许跨域访问时响应方携带哪些非 CORS 规范 以外的 Header。如果你有多个 Header,请使用 , 分割。当 allow_credential 为 false 时,可以使用 * 来表示允许任意 Header。如果不设置,插件不会修改 `Access-Control-Expose-Headers` 头,详情请参考 [Access-Control-Expose-Headers - MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)|
4141
| max_age | integer || 5 | 浏览器缓存 CORS 结果的最大时间,单位为秒。在这个时间范围内,浏览器会复用上一次的检查结果,`-1` 表示不缓存。请注意各个浏览器允许的最大时间不同,详情请参考 [Access-Control-Max-Age - MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age#directives)|
4242
| allow_credential | boolean || false | 是否允许跨域访问的请求方携带凭据(如 Cookie 等)。根据 CORS 规范,如果设置该选项为 `true`,那么将不能在其他属性中使用 `*`|
4343
| allow_origins_by_regex | array || nil | 使用正则表达式数组来匹配允许跨域访问的 Origin,如 `[".*\.test.com$"]` 可以匹配任何 `test.com` 的子域名。如果 `allow_origins_by_regex` 属性已经指定,则会忽略 `allow_origins` 属性。 |
@@ -104,7 +104,6 @@ curl http://127.0.0.1:9080/hello -v
104104
< Access-Control-Allow-Origin: *
105105
< Access-Control-Allow-Methods: *
106106
< Access-Control-Allow-Headers: *
107-
< Access-Control-Expose-Headers: *
108107
< Access-Control-Max-Age: 5
109108
...
110109
```

t/plugin/cors.t

+1-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ Access-Control-Allow-Origin: *
235235
Vary:
236236
Access-Control-Allow-Methods: *
237237
Access-Control-Allow-Headers: *
238-
Access-Control-Expose-Headers: *
238+
Access-Control-Expose-Headers:
239239
Access-Control-Max-Age: 5
240240
Access-Control-Allow-Credentials:
241241

t/plugin/cors3.t

+6-6
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ Access-Control-Allow-Origin: https://test.com
185185
Vary: Via, Origin
186186
Access-Control-Allow-Methods: *
187187
Access-Control-Allow-Headers: *
188-
Access-Control-Expose-Headers: *
188+
Access-Control-Expose-Headers:
189189
Access-Control-Max-Age: 5
190190
Access-Control-Allow-Credentials:
191191
@@ -204,7 +204,7 @@ Access-Control-Allow-Origin: https://domain.com
204204
Vary: Via, Origin
205205
Access-Control-Allow-Methods: *
206206
Access-Control-Allow-Headers: *
207-
Access-Control-Expose-Headers: *
207+
Access-Control-Expose-Headers:
208208
Access-Control-Max-Age: 5
209209
Access-Control-Allow-Credentials:
210210
@@ -276,7 +276,7 @@ Access-Control-Allow-Origin: https://domain.com
276276
Vary: Via, Origin
277277
Access-Control-Allow-Methods: *
278278
Access-Control-Allow-Headers: *
279-
Access-Control-Expose-Headers: *
279+
Access-Control-Expose-Headers:
280280
Access-Control-Max-Age: 5
281281
Access-Control-Allow-Credentials:
282282
@@ -295,7 +295,7 @@ Access-Control-Allow-Origin: https://sub.domain.com
295295
Vary: Via, Origin
296296
Access-Control-Allow-Methods: *
297297
Access-Control-Allow-Headers: *
298-
Access-Control-Expose-Headers: *
298+
Access-Control-Expose-Headers:
299299
Access-Control-Max-Age: 5
300300
Access-Control-Allow-Credentials:
301301
@@ -348,7 +348,7 @@ Access-Control-Allow-Origin: http://foo.example.org
348348
Vary: Origin
349349
Access-Control-Allow-Methods: *
350350
Access-Control-Allow-Headers: *
351-
Access-Control-Expose-Headers: *
351+
Access-Control-Expose-Headers:
352352
Access-Control-Max-Age: 5
353353
Access-Control-Allow-Credentials:
354354
@@ -417,6 +417,6 @@ hello world
417417
Access-Control-Allow-Origin: https://domain.com
418418
Access-Control-Allow-Methods: *
419419
Access-Control-Allow-Headers: *
420-
Access-Control-Expose-Headers: *
420+
Access-Control-Expose-Headers:
421421
Access-Control-Max-Age: 5
422422
Access-Control-Allow-Credentials:

t/plugin/cors4.t

+110
Original file line numberDiff line numberDiff line change
@@ -639,3 +639,113 @@ Access-Control-Allow-Headers: request-h
639639
Access-Control-Expose-Headers: expose-h
640640
Access-Control-Max-Age: 10
641641
Timing-Allow-Origin: http://testurl.domain.com
642+
643+
644+
645+
=== TEST 26: set route ( expose_headers not specified )
646+
--- config
647+
location /t {
648+
content_by_lua_block {
649+
local t = require("lib.test_admin").test
650+
local code, body = t('/apisix/admin/routes/1',
651+
ngx.HTTP_PUT,
652+
[[{
653+
"plugins": {
654+
"cors": {
655+
"allow_credential": true,
656+
"allow_headers": "**",
657+
"allow_methods": "**",
658+
"allow_origins": "**",
659+
"expose_headers": "",
660+
"max_age": 3500
661+
}
662+
},
663+
"upstream": {
664+
"nodes": {
665+
"127.0.0.1:1980": 1
666+
},
667+
"type": "roundrobin"
668+
},
669+
"uri": "/hello"
670+
}]]
671+
)
672+
if code >= 300 then
673+
ngx.status = code
674+
end
675+
ngx.say(body)
676+
}
677+
}
678+
--- request
679+
GET /t
680+
--- response_body
681+
passed
682+
683+
684+
685+
=== TEST 27: remove Access-Control-Expose-Headers match
686+
--- request
687+
GET /hello HTTP/1.1
688+
--- more_headers
689+
Origin: http://sub.domain.com
690+
--- response_headers
691+
Access-Control-Allow-Origin: http://sub.domain.com
692+
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS,CONNECT,TRACE
693+
Access-Control-Expose-Headers:
694+
Access-Control-Allow-Headers:
695+
Access-Control-Max-Age: 3500
696+
Access-Control-Allow-Credentials: true
697+
698+
699+
700+
=== TEST 28: set route ( expose_headers set value )
701+
--- config
702+
location /t {
703+
content_by_lua_block {
704+
local t = require("lib.test_admin").test
705+
local code, body = t('/apisix/admin/routes/1',
706+
ngx.HTTP_PUT,
707+
[[{
708+
"plugins": {
709+
"cors": {
710+
"allow_credential": true,
711+
"allow_headers": "**",
712+
"allow_methods": "**",
713+
"allow_origins": "**",
714+
"expose_headers": "ex-headr1,ex-headr2",
715+
"max_age": 3500
716+
}
717+
},
718+
"upstream": {
719+
"nodes": {
720+
"127.0.0.1:1980": 1
721+
},
722+
"type": "roundrobin"
723+
},
724+
"uri": "/hello"
725+
}]]
726+
)
727+
if code >= 300 then
728+
ngx.status = code
729+
end
730+
ngx.say(body)
731+
}
732+
}
733+
--- request
734+
GET /t
735+
--- response_body
736+
passed
737+
738+
739+
740+
=== TEST 29: Access-Control-Expose-Headers match
741+
--- request
742+
GET /hello HTTP/1.1
743+
--- more_headers
744+
Origin: http://sub.domain.com
745+
--- response_headers
746+
Access-Control-Allow-Origin: http://sub.domain.com
747+
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS,CONNECT,TRACE
748+
Access-Control-Expose-Headers: ex-headr1,ex-headr2
749+
Access-Control-Allow-Headers:
750+
Access-Control-Max-Age: 3500
751+
Access-Control-Allow-Credentials: true

0 commit comments

Comments
 (0)