Clickjacking in Rails with Mutual Tls
Clickjacking in Rails with Mutual Tls — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an attacker tricks a user into interacting with a hidden or disguised element inside an invisible iframe. In a Rails application protected by Mutual TLS (mTLS), the presence of mTLS does not prevent clickjacking. mTLS ensures the client and server authenticate each other with X.509 certificates during the TLS handshake, but it operates at the transport layer and does not enforce how the response is rendered or embedded by the client browser.
When a Rails app uses mTLS, browsers that accept the client certificate still process standard HTTP responses. If the app sets neither X-Frame-Options nor Content-Security-Policy (frame-ancestors), an attacker can embed the authenticated page inside an iframe and overlay interactive elements. Even with mTLS providing strong transport identity, an authenticated session established via client certificates can be hijacked in this embedded context, leading to unauthorized actions performed on behalf of the client.
The combination of mTLS and missing frame protection is especially risky in workflows where sensitive operations are performed after client certificate authentication. For example, a banking flow that uses mTLS to identify the device or user and then shows a confirmation page without frame protection could be embedded on a malicious site, with hidden forms or buttons triggering transfers. The mTLS layer confirms the client’s identity but does nothing to stop the browser from rendering the page in a hostile frame, so the defense gap is in response headers and CSP, not in the TLS configuration.
Because middleBrick scans the unauthenticated attack surface, it can detect missing anti-clickjacking headers and weak CSP frame policies regardless of whether mTLS is in use. Its LLM/AI Security checks also verify whether outputs expose sensitive information that could be leveraged in social engineering alongside clickjacking scenarios.
Mutual Tls-Specific Remediation in Rails — concrete code fixes
Remediation focuses on two layers: ensuring mTLS is correctly configured in Rails and enforcing strict framing rules to prevent clickjacking. Rails does not enforce mTLS natively; it is typically handled at the web server or load balancer (e.g., nginx, HAProxy, or cloud endpoints). The application should still set strong CSP and X-Frame-Options headers as a safety net.
For mTLS in Rails, a common pattern is to validate the client certificate in a before action and map it to a user or device. Below is a controller example that reads the client certificate from the request and performs basic validation:
class ApplicationController < ActionController::Base
before_action :authenticate_client_certificate
private
def authenticate_client_certificate
cert = request.env['SSL_CLIENT_CERT']
return head :forbidden unless cert
# Example: verify certificate subject or fingerprint against an allowlist
fingerprint = OpenSSL::Digest::SHA256.hexdigest(cert.to_der)
allowed_fingerprints = ENV.fetch('ALLOWED_CLIENT_CERT_FINGERPRINTS', '').split(',')
unless allowed_fingerprints.map(&:strip).include?(fingerprint)
head :forbidden
end
end
end
This example uses the SSL_CLIENT_CERT variable, which is typically provided by the web server when mTLS is terminated upstream and the certificate is forwarded to the app. You should adjust the environment variable name to match your server configuration (e.g., SSL_CLIENT_VERIFY and SSL_CLIENT_VERYIFY can indicate successful verification).
To prevent clickjacking, set the following headers in a Rails initializer or a base controller. Combining CSP frame-ancestors with X-Frame-Options ensures broad browser coverage:
class ApplicationController < ActionController::Base
before_action :set_security_headers
private
def set_security_headers
response.headers['X-Frame-Options'] = 'DENY'
response.headers['Content-Security-Policy'] = "frame-ancestors 'none'; default-src 'self'"
end
end
If specific pages need to be embeddable by trusted origins, adjust CSP accordingly:
response.headers['Content-Security-Policy'] = "frame-ancestors 'self' https://trusted.example.com; default-src 'self'"
For completeness, ensure your web server enforces mTLS. An example nginx snippet is provided for context (not executed by Rails), showing how to request and verify client certificates:
server {
listen 443 ssl;
ssl_certificate /path/to/server.crt;
ssl_certificate_key /path/to/server.key;
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client on;
location / {
proxy_pass http://rails_app;
proxy_set_header SSL_CLIENT_CERT $ssl_client_cert;
}
}
middleBrick can validate these configurations by scanning your endpoints and checking for the presence of frame-protection headers and the absence of overly permissive CSP frame rules, even when mTLS is in place.