Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't my CORS configuration causing the server to filter incoming requests? How can I make the server only accept requests from a specific origin?

I'd like my Rails 5 API-only app, for now running on http://localhost:3000, to only accept requests from my NodeJS front-end app, for now running on http://localhost:8888.

So I configured /config/initializers/cors.rb like this:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "http://localhost:8888"
    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

And I wrote this test:

#/spec/request/cors_request_spec.rb

RSpec.feature "CORS protection", type: :request do
  it "should accept a request from a whitelisted domain" do
    get "/api/v1/bodies.json", nil, "HTTP_ORIGIN": "http://localhost:8888"
    expect(response.status).to eql(200)
  end
  it "should reject a request from a non-whitelisted domain" do
    get "/api/v1/bodies.json", nil, "HTTP_ORIGIN": "https://foreign.domain"
    expect(response.status).to eql(406)
  end
end

The first test is passing as expected. But the second is failing with a response code of 200. Why?

(I'm not wed to a 406 response code by the way; just one that indicates the request will not be fulfilled.)

like image 559
steven_noble Avatar asked Nov 29 '22 22:11

steven_noble


1 Answers

CORS configuration won’t prevent the server from accepting requests based on the value of the Origin request header. You can’t do that just through CORS configuration.

When you configure CORS support on a server, all that the server does differently is just to send the Access-Control-Allow-Origin response header and other CORS response headers.

Actual enforcement of CORS restrictions is done only by browsers. It’s not enforced by servers.

The way CORS works is: regardless of any CORS configuration you make on the server side, the server continues accepting requests from all clients and origins it otherwise would—and so all clients from all origins continue geting responses from the server just as they otherwise would.

So even when you see an error in your browser devtools that a cross-origin request from your frontend JavaScript code failed, you’ll still be able to see the response in browser devtools.

But just because your browser can see the response doesn’t mean the browser will expose it to your frontend code. Browsers only expose responses from cross-origin requests to frontend code running at a particular origin if the server the request was sent to opts-in to allowing the request by responding with an Access-Control-Allow-Origin header which OKs that origin.

So for any requests with an Origin request header matching https://foreign.domain, the configuration snippet in the question should cause browsers to emit a message on the client side saying http://localhost:3000/api/v1/bodies.json can’t be loaded because there’s no Access-Control-Allow-Origin response header in the response (because your configuration causes the server to only send that header in responses to your whitelisted origins).

But that’s all you can do through CORS. You can’t prevent the server side from accepting and responding to requests from particular origins just by doing any CORS configuration on the server side. If you want to do that, you need to do it using something other than just CORS.

like image 129
sideshowbarker Avatar answered Dec 05 '22 18:12

sideshowbarker