cyu / rack-cors

Rack Middleware for handling Cross-Origin Resource Sharing (CORS), which makes cross-origin AJAX possible.
MIT License
3.26k stars 263 forks source link

Access Control Headers are not added when using multiple origins and Rails 4.2 and Rack-Cors 1.0.x #187

Closed digerata closed 5 years ago

digerata commented 5 years ago

Hi,

I was going through and adding rack-cors to an older project, hence the reason we are on Rails 4.2. But I hit an issue where my configuration of rack-cors was not resulting in Access-* headers being sent back in a response. My Rack Cors configuration:

application.rb:

config.middleware.insert_before 0, "Rack::Cors" do
  allow do
    origins 'host1.pantheonsite.io', 'host2.pantheonsite.io', 'host3.io', /[\-\.\w]+\.host4\.io$/, /[\-\.\w]+\.host5\.com$/, /[\-\.\w]+\.host6\.net$/
    resource '*', headers: :any, methods: [:get, :post, :options]
  end
end

Gemfile.lock rack-cors version: 1.0.3

I startup the server and then run via curl:

$ curl -I -H 'Origin: host3.io' http://localhost:3000/

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: W/"cb47c1734ff0b7877c7390e5113"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 7e8a08db-26e5-4621-aa6f-ca746b8d
X-Runtime: 0.313786
Vary: Origin
Server: WEBrick/1.3.1 (Ruby/2.4.3/2017-12-14)
Date: Wed, 07 Aug 2019 16:47:49 GMT
Content-Length: 0
Connection: Keep-Alive

But if I revert rack-cors back to 0.4.1, restart the server, and run that same command:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: host3.io
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Expose-Headers:
Access-Control-Max-Age: 1728000
Access-Control-Allow-Credentials: true
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: W/"3649d400697be5e5e55712c821"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: ad2b0fb9-116f-4d39-b837-fde73e5ab
X-Runtime: 0.205086
Vary: Origin
Server: WEBrick/1.3.1 (Ruby/2.4.3/2017-12-14)
Date: Wed, 07 Aug 2019 16:52:29 GMT
Content-Length: 0
Connection: Keep-Alive

If I go back to 1.0.3 and then change my origins to the following:

origins '*'

I get the correct behavior. The Access-* headers are added. So maybe it is multiple hosts that is the problem? But if I change origins to:

origins 'host3.io'

I get the incorrect behavior and there are no Access-* headers present.

Given this issue: https://github.com/cyu/rack-cors/issues/143 it does feel like there is something broken here in later versions and that maybe it isn't a Rails version issue.

cyu commented 5 years ago

Hi @digerata, I was able to reproduce this issue. The issue comes from an uncommon Origin: header being passed. The common form of an origin header has a scheme / protocol defined. If I change the above the curl command to curl -I -H 'Origin: http://host3.io' http://localhost:3000/, I get result I'm expecting. Specification seems to suggest scheme is the accepted standard: https://tools.ietf.org/id/draft-abarth-origin-03.html#rfc.section.2

The change stems from updates of how we handle origin matching when an origin is defined without a scheme. We changed it to not match protocols outside of HTTP/HTTPS. Also excluding an exact match was unintentional, and would consider changing it if it makes sense. A workaround would be to define your origin as an Regex, which would bypass the scheme addition above.

Is not defining a scheme in your test a real use case, or was it an error in how you were testing?

digerata commented 5 years ago

Hi @cyu

Thanks for the response. I think that was an error in how things were being tested that was copied over from our prod environment. In short, both!

Thanks for clearing this up!

phikes commented 5 years ago

A quick note for anyone researching this issue as well: We are sending requests from a mobile app. The scheme is set to ionic://localhost (it is not changeable to http(s)) which also lead to rack-cors not permitting the request/sending the correct response.

A workaround would be to define your origin as an Regex, which would bypass the scheme addition above.

Helped us very much, thanks @cyu !

For the sake of completeness this is the regex we used (good hint about generous regexes in the readme):

%r{\Aionic://localhost\Z}