Clickjacking in Grape
How Clickjacking Manifests in Grape
Clickjacking in Grape applications occurs when an API endpoint returns HTML content without proper frame-busting headers, allowing malicious sites to embed your Grape API responses in invisible iframes. This is particularly dangerous when Grape endpoints return HTML, JSON that gets interpreted as HTML, or when API responses are rendered directly in browsers.
The most common Grape clickjacking scenario involves API endpoints that serve HTML content without X-Frame-Options headers. For example:
class API::V1::Users < Grape::API
format :html
get '/profile' do
# Returns user profile HTML without frame protection
erb :profile, locals: { user: current_user }
end
endAttackers can embed this endpoint in an iframe on their malicious site, tricking users into clicking on hidden elements. A typical attack might overlay invisible buttons on legitimate UI elements, causing users to unknowingly perform actions like changing passwords or transferring funds.
Another Grape-specific vulnerability arises when using Grape's built-in HTML rendering with format detection. Consider this endpoint:
class API::V1::Settings < Grape::API
get '/preferences' do
# Accepts format parameter, potentially returning HTML
content_type :html if params[:format] == 'html'
erb :preferences, locals: { settings: user_settings }
end
endIf an attacker crafts a request that forces HTML output (via Accept headers or format parameters), they can embed the response in an iframe and perform UI redress attacks.
Grape's flexibility with content types can also create clickjacking opportunities. When an API endpoint supports multiple formats and doesn't properly validate the response type, attackers can manipulate requests to receive HTML content that gets framed:
class API::V1::Account < Grape::API
get '/status' do
# Potentially returns HTML based on request headers
if request.accept.first == 'text/html'
erb :account_status, locals: { account: current_account }
else
{ status: 'active' }
end
end
endThe core issue is that Grape applications must explicitly protect against framing, as the framework itself doesn't enforce frame-busting headers by default.
Grape-Specific Detection
Detecting clickjacking vulnerabilities in Grape applications requires examining both the code structure and runtime behavior. Start by reviewing your Grape API endpoints for HTML content delivery:
# Scan for endpoints that might return HTML
Grape::API.subclasses.each do |api_class|
api_class.endpoints.each do |endpoint|
# Check if endpoint uses erb, haml, or other template rendering
if endpoint.instance_variable_get(:@block).source.include?('erb') ||
endpoint.instance_variable_get(:@block).source.include?('haml')
puts "Potential clickjacking endpoint: #{endpoint.path}"
end
end
endThis script identifies endpoints that use template rendering, which are prime candidates for clickjacking if they lack proper headers.
For runtime detection, middleBrick's black-box scanning approach is particularly effective for Grape applications. The scanner tests unauthenticated endpoints by attempting to frame API responses and checking for X-Frame-Options headers:
# Scan a Grape API endpoint for clickjacking vulnerabilities
middlebrick scan https://api.example.com/v1/profile --format=jsonThe scanner checks for multiple clickjacking indicators specific to Grape applications:
- X-Frame-Options header presence and values (DENY, SAMEORIGIN)
- Content-Security-Policy frame-ancestors directive
- HTML content types in API responses
- Template rendering usage in endpoint blocks
- Format negotiation that could lead to HTML responses
middleBrick also performs active clickjacking testing by attempting to load API responses in iframes and detecting if the content renders successfully. This is particularly valuable for Grape applications where the attack surface might include HTML-generating endpoints mixed with pure JSON APIs.
For comprehensive coverage, integrate middleBrick into your CI/CD pipeline using the GitHub Action:
- name: Run middleBrick Security Scan
uses: middlebrick/middlebrick-action@v1
with:
url: https://api.example.com
fail-on-severity: high
token: ${{ secrets.MIDDLEBRICK_TOKEN }}This ensures clickjacking vulnerabilities are caught before deployment, especially important for Grape applications where HTML endpoints might be added by different team members over time.
Grape-Specific Remediation
Remediating clickjacking in Grape applications involves multiple layers of protection. The most straightforward approach is adding X-Frame-Options headers at the API level:
module Grape
class API
# Add X-Frame-Options header to all responses
before do
header 'X-Frame-Options', 'DENY'
end
end
endThis before filter ensures all Grape endpoints include the anti-framing header, preventing any external site from embedding your API responses.
For more granular control, apply headers at the endpoint level:
class API::V1::Users < Grape::API
before do
header 'X-Frame-Options', 'SAMEORIGIN'
header 'Content-Security-Policy', "frame-ancestors 'self'"
end
get '/profile' do
erb :profile, locals: { user: current_user }
end
endThe Content-Security-Policy header provides modern frame protection and works alongside X-Frame-Options for maximum compatibility.
When dealing with HTML-generating endpoints, consider separating API logic from presentation:
class API::V1::Users < Grape::API
# API-only endpoints (no HTML)
get '/:id' do
present User.find(params[:id]), with: API::Entities::User
end
# Separate namespace for HTML endpoints
class Web < Grape::API
before do
header 'X-Frame-Options', 'DENY'
end
get '/:id' do
erb :user_profile, locals: { user: User.find(params[:id]) }
end
end
endThis separation ensures that endpoints serving HTML content have proper frame protection while maintaining clean API design.
For Grape applications using Rack middleware, you can add clickjacking protection globally:
use Rack::Protection::FrameOptions
class API < Grape::API
# Your endpoints here
endThe Rack::Protection middleware automatically adds appropriate headers to all responses, providing a defense-in-depth approach.
Finally, audit your Grape application for endpoints that might inadvertently return HTML:
# Security audit script
Grape::API.subclasses.each do |api_class|
api_class.endpoints.each do |endpoint|
block_source = endpoint.instance_variable_get(:@block).source
if block_source.include?('erb') || block_source.include?('haml') ||
block_source.include?('render') || block_source.include?('format')
puts "HTML endpoint detected: #{api_class.name}##{endpoint.path}"
# Check if frame protection is present
unless block_source.include?('X-Frame-Options') ||
block_source.include?('Content-Security-Policy')
puts " ⚠️ Missing frame protection"
end
end
end
endRun this audit regularly, especially when new endpoints are added or existing ones are modified, to ensure clickjacking protection remains comprehensive across your Grape API surface.