Cors Wildcard in Rails with Basic Auth
Cors Wildcard in Rails with Basic Auth — how this specific combination creates or exposes the vulnerability
Cross-origin resource sharing (CORS) misconfigurations are common in Rails APIs. When an endpoint uses a permissive CORS wildcard (origins: '*') and also relies on HTTP Basic Authentication, the protection that the browser’s same-origin policy provides is effectively weakened. The wildcard allows any origin to make a request, and when credentials are involved, browsers enforce stricter rules around exposing the response to JavaScript. This combination can lead to unauthorized cross-origin reading of authenticated responses if the server does not explicitly validate and reflect the correct origin and does not handle the Authorization header carefully.
Consider a Rails controller that authenticates via a request header containing Basic credentials:
class Api::V1::ProfilesController < ApplicationController
before_action :authenticate_with_basic_auth
def show
render json: { user: current_user, email: current_user.email }
end
private
def authenticate_with_basic_auth
authenticate_or_request_with_http_basic do |username, password|
User.authenticate_by_credentials(username, password)
end
end
end
If this endpoint also declares a permissive CORS policy:
class ApplicationController < ActionController::Base
before_action :set_cors_headers
private
def set_cors_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
headers['Access-Control-Allow-Credentials'] = 'true'
end
end
There are two specific concerns with this setup. First, Access-Control-Allow-Credentials: true combined with Access-Control-Allow-Origin: * is explicitly disallowed by the CORS specification; browsers will reject the response in a frontend JavaScript context. Second, even if the origin were restricted, simply reflecting the Origin header back as Access-Control-Allow-Origin without strict validation can enable a bypass where a malicious site orchestrates a request that includes credentials, and the response is exposed to that site if the server fails to check a strict allowlist. This can expose data protected by Basic Auth to a malicious website via an XMLHttpRequest or fetch call initiated from the attacker’s page.
An attacker does not need to break the Basic Authentication mechanism itself; they need a way to trick a victim’s browser into sending credentials to your API from an unauthorized origin and then read the response. If your CORS configuration does not tightly control which origins are allowed, and if the server erroneously trusts the Origin header, an attacker can chain a reflected origin with a crafted frontend to harvest sensitive profile or account data returned under Basic Auth protection.
Basic Auth-Specific Remediation in Rails — concrete code fixes
Remediation focuses on tightening CORS policy and ensuring that credentials and origins are handled explicitly. Avoid using a wildcard when credentials or sensitive data are involved. Instead, implement a strict allowlist of origins and ensure that the server only reflects origins that are explicitly permitted.
First, replace the wildcard with a controlled allowlist. In production, read allowed origins from an environment variable so that configuration can differ between environments:
class ApplicationController < ActionController::Base
ALLOWED_ORIGINS = ENV.fetch('ALLOWED_ORIGIN', 'https://app.yourdomain.com').split(',').map(&:strip)
before_action :set_cors_headers
private
def set_cors_headers
origin = request.headers['Origin']
if ALLOWED_ORIGINS.include?(origin)
headers['Access-Control-Allow-Origin'] = origin
headers['Access-Control-Allow-Credentials'] = 'true'
end
headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
end
end
This pattern ensures that the Origin is validated against a known list before being echoed back. Never set Access-Control-Allow-Origin to the value of the incoming Origin header without checking it.
Second, keep using HTTP Basic Authentication for your API as shown earlier, but ensure that your CORS configuration does not encourage mixed security models. For preflight requests, respond with appropriate headers but do not perform authentication; keep authentication checks only on actual requests:
class ApplicationController < ActionController::Base
before_action :set_cors_headers, only: [:options]
before_action :authenticate_with_basic_auth, only: [:show, :update, :destroy]
def options
head :ok
end
private
When testing or integrating with tools, you can verify that the headers are correct with a simple curl command:
curl -H "Origin: https://app.yourdomain.com" -I https://api.yourdomain.com/api/v1/profiles/1
Observe that Access-Control-Allow-Origin reflects only the origin you explicitly allow, and that credentials are not unnecessarily exposed to wildcard origins. For broader adoption, use the Pro plan of middleBrick to add continuous monitoring to your API security posture; its dashboard lets you track CORS and authentication-related findings over time and can be integrated into CI/CD pipelines with the GitHub Action to fail builds if risky configurations are detected.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |