MEDIUM clickjackinggrape

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
end

Attackers 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
end

If 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
end

The 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
end

This 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=json

The 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
end

This 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
end

The 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
end

This 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
end

The 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
end

Run 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.

Frequently Asked Questions

Can clickjacking affect JSON-only Grape APIs?
Yes, if the JSON response can be interpreted as HTML by browsers. Some APIs return JSON that, when rendered in a browser, might execute scripts or display content in ways that enable clickjacking. Always include X-Frame-Options headers regardless of content type.
Does middleBrick scan for clickjacking in Grape applications?
Yes, middleBrick's black-box scanning tests whether API responses can be framed by attempting to load them in iframes and checking for anti-framing headers. It specifically looks for X-Frame-Options and Content-Security-Policy headers in Grape API responses.