Clickjacking in Rails (Ruby)
Clickjacking in Rails with Ruby — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side vulnerability where an attacker tricks a user into clicking UI elements that are invisible or disguised within an embedded frame (iframe). In a Ruby on Rails application, this commonly occurs when views do not enforce that the app cannot be framed by external origins. Rails includes a default X-Frame-Options header only in certain configurations, and if developers override or omit this protection, pages can be loaded inside an attacker-controlled iframe.
Ruby on Rails’ default settings historically provided some protection, but the framework does not enforce a strict framing policy globally. If a controller action renders a view without explicitly setting a frame option, an attacker can embed the page in a malicious site and use CSS/JavaScript to overlay invisible controls or misleading prompts. This is especially risky in Rails apps that render sensitive actions (like confirmations, deletes, or financial approvals) without considering framing.
Another contributing factor is the use of Ruby gems or legacy code that may strip or ignore security headers. For example, a developer might use config.action_dispatch.default_headers to set custom headers but inadvertently omit X-Frame-Options or Content-Security-Policy. Additionally, Rails APIs and view components that return HTML partials without framing safeguards can expose endpoints to being framed, even when the main application sets protections inconsistently across subdomains.
The interaction between Ruby’s server-side rendering and the browser’s interpretation of frame rules means misconfigurations in Rails views, routes, or middleware can lead to exploitable clickjacking surfaces. Attack patterns often involve luring a logged-in user to a page where an invisible iframe triggers state-changing requests via GET or POST, leveraging CSRF-like behavior if other protections are weak.
Ruby-Specific Remediation in Rails — concrete code fixes
To defend against clickjacking in Rails, explicitly set frame-denial headers in your application or per-controller. The most direct approach is to use ActionController::Base.default_headers to ensure every response includes a strong framing policy. Below is a Ruby example that sets X-Frame-Options to DENY globally in an initializer or application_controller.rb.
# config/initializers/frame_options.rb
Rails.application.config.action_dispatch.default_headers = {
'X-Frame-Options' => 'DENY',
'Content-Security-Policy' => "frame-ancestors 'none'"
}
If you need to allow framing from specific trusted sources, use SAMEORIGIN for same-domain embedding or provide a CSP frame-ancestors directive with explicit origins. The following example demonstrates per-controller framing rules in Ruby, useful when certain public pages may be embedded but admin sections must be protected.
class Admin::DashboardController < ApplicationController
before_action :set_frame_options
def index
# admin dashboard logic
end
private
def set_frame_options
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
end
end
For modern Rails applications using API mode or JSON responses, ensure that HTML responses include these headers and that API endpoints do not render views that could be framed. You can also conditionally apply headers based on the request format.
class ApplicationController < ActionController::Base
before_action :apply_frame_protection
private
def apply_frame_protection
if response.content_type == 'text/html'
response.headers['X-Frame-Options'] = 'DENY'
response.headers['Content-Security-Policy'] = "frame-ancestors 'none'"
end
end
end
When testing, verify headers using browser developer tools or automated checks to confirm that framing is blocked. Combine these Ruby-level protections with secure coding practices such as avoiding GET endpoints for state-changing actions and ensuring CSRF tokens are present, which complements framing defenses.