Cors Wildcard in Grape with Mutual Tls
Cors Wildcard in Grape with Mutual Tls — how this specific combination creates or exposes the vulnerability
A CORS wildcard (origins: '*') in a Grape API combined with Mutual TLS (client certificate verification) can produce a misleading security posture. Individually, each control appears strong: the server requires a valid client certificate, and the API restricts origins. However, the wildcard origin allows any browser-based JavaScript origin to make requests, which can undermine the intended isolation when certificates are used for authorization rather than strict network boundary enforcement.
In practice, browsers enforce CORS in the context of the requesting origin. If a frontend hosted on https://example.com presents a valid client certificate (e.g., via a corporate-managed browser or a user-installed root CA), the request will succeed because CORS permits the wildcard and the TLS handshake succeeds. The critical nuance is that CORS is a browser security mechanism; it does not apply to non-browser clients (curl, Postman, custom scripts). Meanwhile, Mutual TLS operates at the transport layer and is enforced before the application layer (Grape) sees the request.
Risks emerge in these scenarios:
- Cross-origin credential leakage: If the wildcard CORS policy is permissive and client certificates are issued broadly (e.g., to end-user devices), a malicious site could initiate authenticated requests from a user’s browser, potentially leveraging stored client certificates if they are accessible to the browser’s certificate store.
- Misplaced trust in origin checks: Relying on CORS to restrict which origins can call backend APIs is insufficient when Mutual TLS is used for authorization. CORS headers returned by the server do not prevent non-browser clients from sending requests with valid certificates.
- Certificate mapping confusion: Some implementations map client certificate fields (e.g., CN, email) to user roles. If CORS is open, any origin that can present a valid certificate can exploit permissive origin policies to interact with the API from a browser context, bypassing same-origin protections expected by the backend.
Crucially, middleBrick’s security checks flag this combination during scans because the runtime findings may reveal CORS responses with Access-Control-Allow-Origin: * alongside successful TLS client authentication. This does not indicate a tool failure; it highlights a configuration mismatch where CORS and Mutual TLS are not aligned in their security assumptions.
Mutual Tls-Specific Remediation in Grape — concrete code fixes
Remediation focuses on aligning CORS policy with the trust boundary established by Mutual TLS. Avoid broad origins when client certificates are used for authorization. Instead, specify exact origins and enforce certificate-to-user mapping carefully.
1) Configure CORS with explicit origins
Replace the wildcard with specific frontend origins. This ensures browser-based requests are only accepted from known, trusted sources.
require 'grape'
require 'rack/cors'
class MyAPI < Grape::API
# CORS configuration via Rack middleware (outside Grape routes)
# This block is typically placed in config.ru or a Rack configuration file
end
# Example: config.ru
use Rack::Cors do
allow do
origins 'https://app.yourcompany.com', 'https://staging.yourcompany.com'
resource '*',
headers: :any,
methods: [:get, :post, :put, :delete, :options],
expose: ['X-Rate-Limit-Limit', 'X-Rate-Limit-Remaining'],
max_age: 600
end
end
run MyAPI
2) Enforce Mutual TLS in Grape with certificate validation
Use the ssl directive in your Rack server (e.g., Puma) to require client certificates, and optionally validate them within Grape using the request environment.
# config.ru or Puma configuration snippet
ssl_options = {
verify_mode: OpenSSL::SSL::VERIFY_PEER,
cert_store: OpenSSL::X509::Store.new,
# Provide paths to CA certificates that sign client certificates
ca_file: '/path/to/ca-bundle.crt'
}
# In Puma config (config/puma.rb or via CLI):
# ssl_bind '0.0.0.0', '8443', ssl_options
class MyAPI < Grape::API
before do
client_cert = request.env['SSL_CLIENT_CERT']
unless client_cert&.verify
error!('Client certificate verification failed', 403)
end
# Optional: extract identity from certificate
cert = OpenSSL::X509::Certificate.new(client_cert)
# Map certificate fields to user/role, e.g., via CN or SAN
# Avoid relying solely on subject CN for authorization without additional checks
end
get :secure_data do
{ message: 'Access granted with valid client certificate' }
end
end
3) Tighten origin and certificate mapping
If you must support multiple origins, ensure each origin is explicitly listed. Avoid broad certificate issuance policies that allow any authenticated certificate to access any origin from any browser context.
# Example of strict per-origin configuration
use Rack::Cors do
allow do
origins 'https://app.yourcompany.com'
resource '/api/*',
headers: :any,
methods: [:get],
max_age: 600
end
allow do
origins 'https://internal-dashboard.yourcompany.com'
resource '/admin/*',
headers: :any,
methods: [:post, :put, :delete],
max_age: 300
end
end
Combine this with certificate revocation checks (e.g., CRL or OCSP) and short-lived certificates to reduce the impact of compromised credentials. middleBrick’s scans can validate that CORS headers are not overly permissive when client certificates are enforced, providing actionable feedback on misconfigurations.
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 |