# Rack::Cors Configuration Tricks

cyu’s Rack::Cors middleware is rather handy if want to control your CORS (Cross-Origin Resource Sharing) settings in a Ruby-on-Rails project. Previously, there was a fairly major issue where :credentials => true was the default (which you generally do not want), but there were also some more complicated tweaks that I wanted to make.

One problem I recently had to deal with was wanting to:

• Allow CORS connections from arbitrary domains (this site functions as an API)
• Do not allow CORS from http domains at all
• Only allow cookies (Access-Control-Allow-Credentials) to be sent for sibling subdomains
• Prevent cookies from being sent from specific sibling subdomains (that are actually run by a third party)
• On development (non-production) versions of the site, allow credentials from localhost

In short, here is the rough configuration:

# The main domain, this might be different per environment
domain = 'example.com'

# These subdomains are not trusted and should not be allowed to authenticate with cookies
cors_third_party_subdomains = [
'blog',
'shop',
'support'
].join('|')

# These are the Rack::Cors settings that we want to set, first for all domains and then for trusted ones
:methods => [:get, :post, :put, :delete, :options, :patch],
:credentials => false
}

# This is the actual Rack::Cors configuration
config.middleware.insert_before 0, Rack::Cors do
# Third party subdomains should not get cookies in case of XSS
allow do
origins /^https:\/\/(#{cors_third_party_subdomains})\.#{domain}$/ resource '*', cors_headers end # We only want allow-credentials to be true for our own requests # Otherwise you'll need to supply or other non-cookie credentials allow do origins /^https:\/\/(|[^.]+\.)#{domain}$/
end

# All other requests made over https allow CORS without credentials
allow do
origins /^https:\/\//
end

# Allow connections from localhost on non-prod environments as internal requests
unless Rails.env.production?
allow do
origins /^(https?:\/\/)?localhost(:\d+)?\$/
end
end
end


I’m sure there are other ways to deal with this, but I spent a little whiel working on it and finally got just what I wanted.

A few gotchas to keep in mind:

• You need to make sure to specify the entire origin to prevent suffix attacks (such as example.com.evil.com).
• You also should specific the scheme to prevent downgrade attacks.
• You should turn off credentials for any domain you don’t trust.

Give it a try.