apisix
apisix copied to clipboard
bug: plugin:cors: config expose_headers using "**" has no handle when allow_credential set true
Current Behavior
Background: plugin cors
enabled, allow_credential
setting true
Goal: expose all headers to frontend
detail:
According to the documention, the config expose_headers
of plugin cors set to '**', but it does not work.
Then I read the code of cors plugin finding no handle with '**' of expose_headers
https://github.com/apache/apisix/blob/master/apisix/plugins/cors.lua#L186-L241
Expected Behavior
expose all headers
Error Logs
No response
Steps to Reproduce
enable plugin cors with allow_credential config true and expose_headers true
Environment
- APISIX version (run
apisix version
): apache/apisix:2.14.1-alpine - Operating system (run
uname -a
): Ubuntu 20.04.3 with docker - OpenResty / Nginx version (run
openresty -V
ornginx -V
): nginx version: openresty/1.19.9.1
theoretically all version
We may check if the conf.expose_headers
exists then set this header.
if we set Access-Control-Allow-Credentials
to true
, we cannot use '*' to allow all for the other attributes, like Access-Control-Expose-Headers
?
so response headers like:
Access-Control-Expose-Headers: *
Access-Control-Allow-Credentials: true
is not available.
ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers#directives
@tzssangglass I think @nthsky wants to hide the Access-Control-Allow-Credentials
header?
@tzssangglass @tokers
I use '**' not '*' recording to the document to expose all headers.(Sorry I may not have described it clearly before, the '**' is rendered in bold)
The problem is it doesn't work.
And I read the code of plugin finding the plugin does not handle '**' of expose_headers
.
I wanna know whether it's the document's problem(the plugin will not handle '**').
Or maybe it will be implemented in some version?
@nthsky This is really supported and **
is just treated as normal values (at the code base level).
Could you provide the reproduce steps?
I mean it's being treated as a normal value now. But I think it's expected to expose all headers when using '**', just like the other options, for example: reproduce steps:
- enable then cors plugin
- plugin config is like:
"cors": {
"allow_credential": true,
"allow_headers": "**",
"allow_methods": "**",
"allow_origins": "**",
"expose_headers": "**",
"disable": false,
"max_age": 5
}
- start a http server and a frontend web service, here I use gin for backend and axios for frontend. And the http server is behind apisix on port 9080
backend code:
r.POST("/t", func(c *gin.Context) {
c.Header("cool", "cooler")
resp := make(gin.H)
for k, v := range c.Request.Header {
resp[k] = v
}
c.JSON(200, resp)
})
frontend code:
let url = "http://localhost:9080/t"
axios({
method: "post",
url: url,
withCredentials: true,
headers: {
"Authorization": "xxxxxxxx",
},
}).then(res => {
console.log(res)
})
- result is like:
{
"data": {
"Authorization": ["xxxxx"]
......
},
"headers": {
"content-length": "1065",
"content-type": "application/json; charset=utf-8"
}
}
recording to the result,
the request header authorization
is passed through when allow_headers
is '**'
but the response header cool
is not exposed though the expose_headers
is set to '**'.
Recoding to the document I think It is expected to exposed all headers when expose_headers
is '**'.
Recoding to the document I think It is expected to exposed all headers when
expose_headers
is '**'.
Are you saying that the backend should be able to get the header cool: cooler
in the client request under the condition "expose_headers": "**"
?
My reproduction steps are as follows
- upstream is OpenResty, conf:
master_process on;
worker_processes 2;
error_log logs/error.log warn;
pid logs/nginx.pid;
worker_rlimit_nofile 20480;
events {
accept_mutex off;
worker_connections 10620;
}
worker_shutdown_timeout 3;
http {
server {
listen 1980;
access_log off;
location / {
content_by_lua_block {
local get_headers = ngx.req.get_headers
for k, v in pairs(get_headers(100)) do
ngx.say(k .. ": " .. v)
end
}
}
}
}
this prints all the headers of the request.
- config route on APISIX
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"cors": {
"allow_origins": "**",
"allow_methods": "**",
"allow_headers": "**",
"expose_headers": "**",
"madx_age": 5,
"allow_credential": true
}
},
"uri": "/hello",
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
- test
curl http://127.0.0.1:1980/get -H "Foo: Bar" -H "Authorization: Basic Zm9vOmZvbwo="
host: 127.0.0.1:1980
accept: */*
foo: Bar
user-agent: curl/7.29.0
authorization: Basic Zm9vOmZvbwo
Foo:Bar
has been passed to the backend.
The
Access-Control-Expose-Headers
response header allows a server to indicate which response headers should be made available to scripts running in the browser, in response to a cross-origin request.
The header cool: cooler
is response from backend to frontend .I mean the header cool: cooler
should be available to the frontend.
The header
cool: cooler
is response from backend to frontend .I mean the headercool: cooler
should be available to the frontend.
the backend server should set cool: cooler
?
backend code:
r.POST("/t", func(c *gin.Context) {
c.Header("cool", "cooler")
resp := make(gin.H)
for k, v := range c.Request.Header {
resp[k] = v
}
c.JSON(200, resp)
})
backend server has set cool: cooler
- upstream conf
master_process on;
worker_processes 2;
error_log logs/error.log warn;
pid logs/nginx.pid;
worker_rlimit_nofile 20480;
events {
accept_mutex off;
worker_connections 10620;
}
worker_shutdown_timeout 3;
http {
server {
listen 1980;
access_log off;
location / {
content_by_lua_block {
ngx.header["Foo"] = "Bar"
}
}
}
}
test upstream
$ curl http://127.0.0.1:1980/hello -i
HTTP/1.1 200 OK
Server: openresty/1.21.4.1
Date: Mon, 11 Jul 2022 02:01:35 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
Foo: Bar
- route
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"cors": {
"allow_origins": "**",
"allow_methods": "**",
"allow_headers": "**",
"expose_headers": "**",
"madx_age": 5,
"allow_credential": true
}
},
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
- test cors plugin
curl http://127.0.0.1:9080/hello -i
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Date: Mon, 11 Jul 2022 02:02:26 GMT
Foo: Bar
Server: APISIX/2.14.1
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS,CONNECT,TRACE
Access-Control-Max-Age: 5
Access-Control-Expose-Headers: **
Access-Control-Allow-Credentials: true
get Foo: Bar
response header
It's the cross domain problem and curl can not explain problem. You can try following code from origin other than 127.0.0.1 in the browser console
fetch("http://127.0.0.1:9080/hello").then(resp => console.log(Array.from(resp.headers.keys())))
The header Foo: Bar
could not be get from the script.
You will only get content-type
and content-length
header in this case.
Only if Access-Control-Expose-Headers
is set specially Foo
, then the foo header can be get. So I said the **
does not work.
Looks like this is not related to APISIX but the handling of CORS in the broswer.
I can't find the explanation about **
easily. Do you have any materials?
Only if
Access-Control-Expose-Headers
is set speciallyFoo
, then the foo header can be get. So I said the**
does not work.
Looks like that in this case, we should make Access-Control-Expose-Headers
return *
instead of **
, than it would work.
Only if
Access-Control-Expose-Headers
is set speciallyFoo
, then the foo header can be get. So I said the**
does not work.Looks like that in this case, we should make
Access-Control-Expose-Headers
return*
instead of**
, than it would work.
I'm afraid not, since allow_credential
is true
, browser might suffer from another problem.
I can't find the explanation about
**
easily. Do you have any materials?
https://apisix.apache.org/docs/apisix/plugins/cors
exposed_headers
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.
Looks like this is not related to APISIX but the handling of CORS in the broswer.
It's work cors plugin to do, isn't it? The official document above said but I found no handling.
curl http://127.0.0.1:9080/hello -i HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Date: Mon, 11 Jul 2022 02:02:26 GMT Foo: Bar Server: APISIX/2.14.1 Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS,CONNECT,TRACE Access-Control-Max-Age: 5 Access-Control-Expose-Headers: ** Access-Control-Allow-Credentials: true
if here Access-Control-Expose-Headers
is *
not **
, would the browser get Foo: bar
header?
https://github.com/apache/apisix/blob/master/apisix/plugins/cors.lua#L167-L172
when allow-credentials is true, it is not allowed to set other field to *
https://github.com/apache/apisix/blob/master/apisix/plugins/cors.lua#L167-L172
when allow-credentials is true, it is not allowed to set other field to
*
ok, I get it. So far, APISIX returns the header Foo:Bar
, but it is not available in the browser, I don't think this is due to APISIX but browser.
https://github.com/apache/apisix/blob/master/apisix/plugins/cors.lua#L167-L172
when allow-credentials is true, it is not allowed to set other field to
*
ok, I get it. So far, APISIX returns the header
Foo:Bar
, but it is not available in the browser, I don't think this is due to APISIX but browser.
Yes, but we need to know the exact meaning for **
and why it's not effective.
https://github.com/apache/apisix/blob/master/apisix/plugins/cors.lua#L167-L172
when allow-credentials is true, it is not allowed to set other field to
*
ok, I get it. So far, APISIX returns the header
Foo:Bar
, but it is not available in the browser, I don't think this is due to APISIX but browser.
But the cross origin policy is just what browser does and what cors plugin
need to resolve. I think so.
https://github.com/apache/apisix/blob/master/apisix/plugins/cors.lua#L167-L172
when allow-credentials is true, it is not allowed to set other field to
*
ok, I get it. So far, APISIX returns the header
Foo:Bar
, but it is not available in the browser, I don't think this is due to APISIX but browser.Yes, but we need to know the exact meaning for
**
and why it's not effective.
According to the plugin's code, **
is special handling of the plugin.
For example, **
for allow origin
put the origin host to the access-control-allow-origin
.
Other config is similar but just not handle the expose_headers
.
For example,
**
forallow origin
put the origin host to theaccess-control-allow-origin
. Other config is similar but just not handle theexpose_headers
.
when allow_credential
is true
,
- if
allow_headers
is**
,Access-Control-Allow-Origin
would be ignored in response header; - if
expose_headers
is**
,Access-Control-Expose-Headers
would be**
, should we makeAccess-Control-Expose-Headers
ignored asAccess-Control-Allow-Origin
?
I'm not sure, it's just an idea, you can try it on the browser.
This issue has been marked as stale due to 350 days of inactivity. It will be closed in 2 weeks if no further activity occurs. If this issue is still relevant, please simply write any comment. Even if closed, you can still revive the issue at any time or discuss it on the [email protected] list. Thank you for your contributions.
This issue has been closed due to lack of activity. If you think that is incorrect, or the issue requires additional review, you can revive the issue at any time.