Cors Wildcard in Rails with Mutual Tls
Cors Wildcard in Rails with Mutual Tls — how this specific combination creates or exposes the vulnerability
A CORS wildcard origin (Access-Control-Allow-Origin: *) combined with Mutual TLS (mTLS) in a Ruby on Rails application can weaken the intended assurance of client authentication. mTLS requires the client to present a valid certificate during the TLS handshake, which proves possession of a private key. However, CORS is enforced by the browser after the TLS handshake completes. When an endpoint allows any origin via a wildcard, the browser will accept the response from a client that presented a valid mTLS certificate and then allow JavaScript from the permitted origins to read the response. This means the strong client authentication provided by mTLS can be undermined if the response is inadvertently exposed to browser scripts from unauthorized origins.
For example, consider a Rails API that uses config.api_only = true and a custom ApplicationController that sets CORS headers without origin validation. If the server also terminates mTLS at the load balancer or reverse proxy and maps the client certificate to a user identity, the backend may believe the request is authenticated. However, the browser will process the response for any origin allowed by the wildcard. An attacker who controls a page on a different origin could embed an image or script tag, or use fetch with mode: 'cors', and potentially read reflected sensitive information if the response contains data intended only for the mTLS-authenticated client. This violates the principle of same-origin policy layering that defense-in-depth relies on.
In practice, Rails applications often rely on the rack-cors gem to manage headers. A configuration that sets origins '*' while the server expects mTLS creates a mismatch: the server authenticates the client with certificates, but the browser treats the response as shareable across all origins. This is especially risky for endpoints that return sensitive data, such as those protected by OWASP API Top 10 Broken Access Control, because the browser may expose the data to malicious scripts. Even if the mTLS handshake succeeds, the CORS policy must be aligned with the intended audience to prevent unauthorized cross-origin access to authenticated responses.
Real-world attack patterns include credential or token leakage via cross-origin requests where the client certificate is presented but the origin policy is unrestricted. This does not mean mTLS is invalid; it means the application must ensure that CORS rules reflect the same trust boundaries. For endpoints requiring mTLS, origins should be explicitly restricted to known, trusted domains rather than using a wildcard. This preserves the integrity of certificate-based authentication while preventing browser-mediated data exposure.
Mutual Tls-Specific Remediation in Rails — concrete code fixes
To secure a Rails application using mTLS, you must enforce strict origin validation in CORS configuration and ensure the application correctly interprets client certificates. Below are concrete, syntactically correct examples that align CORS policy with mTLS authentication.
1. Configure CORS with specific origins
Replace wildcard origins with explicit domains. This ensures that only trusted frontends can access authenticated responses.
# config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'https://app.example.com', 'https://admin.example.com'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
expose: ['X-Request-Id', 'X-Duration'],
max_age: 1728000
end
end
2. Map client certificate to current user in a before action
Use the client certificate presented during mTLS to identify and authenticate the request. This keeps CORS and authentication consistent within Rails.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
before_action :authenticate_client_from_cert
private
def authenticate_client_from_cert
cert = request.env['SSL_CLIENT_CERT']
if cert.present?
x509 = OpenSSL::X509::Certificate.new(cert)
# Example: map certificate serial to a user in your database
user = User.find_by(certificate_serial: x509.serial.to_s)
if user
# Store user for authorization in downstream actions
@current_user = user
else
render_unauthorized
end
else
render_unauthorized
end
end
def render_unauthorized
render json: { error: 'Unauthorized' }, status: :unauthorized
end
end
3. Enforce mTLS at the proxy or web server and pass identity to Rails
When terminating TLS at a load balancer or reverse proxy, configure it to verify client certificates and forward the identity to Rails via a trusted header. Ensure Rails only accepts this header from trusted proxies.
# config/puma.rb or equivalent server config
# Set trusted headers so Action Dispatch parses them safely
if ENV['TRUSTED_PROXY']
Rails.application.config.action_dispatch.trusted_proxies = [IPAddr.new(ENV['TRUSTED_PROXY'])]
end
# Example header set by the proxy after mTLS verification
# X-SSL-Client-Subject: CN=alice,OU=Engineering,O=Example,C=US
# Rails can read this in a controller when the proxy is trusted
subject = request.headers['HTTP_X_SSL_CLIENT_SUBJECT']
4. Combine CORS and authentication checks in an API controller
For endpoints that require both CORS and mTLS, perform explicit checks and avoid relying solely on wildcard origins.
# app/controllers/api/v1/data_controller.rb
module Api::V1
class DataController < ApplicationController
before_action :authenticate_client_from_cert
def show
# Only allow requests from origins explicitly permitted by CORS
unless allowed_origin?(request.headers['Origin'])
render_unauthorized and return
end
# Proceed with data that is scoped to @current_user
render json: { data: secure_data_for(@current_user) }
end
private
def allowed_origin?(origin)
allowed = ['https://app.example.com', 'https://admin.example.com']
allowed.include?(origin)
end
end
end
These examples demonstrate how to align CORS policy with mTLS authentication in Rails. The key is to avoid wildcard origins when client certificates are used and to explicitly manage which origins can access authenticated responses. This reduces the risk of exposing mTLS-authenticated data to unauthorized browser origins.
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 |