Dns Rebinding in Grape with Mutual Tls
Dns Rebinding in Grape with Mutual Tls — how this specific combination creates or exposes the vulnerability
DNS Rebinding is a client-side attack where a malicious webpage changes the IP address of a domain after initial page load, causing the victim’s browser to send requests to a different host than intended. When combined with Grape, a Ruby micro-framework, and Mutual TLS (mTLS), the interaction can expose or create a security risk if the API endpoint does not properly validate the client identity or origin.
In a typical Grape API secured with mTLS, the server authenticates clients using client certificates during the TLS handshake. This works well for machine-to-machine communication, but if the API also allows requests from browsers (e.g., JavaScript frontends calling the Grape endpoint), DNS Rebinding can bypass intended origin restrictions. An attacker can craft a page that first loads a trusted domain with a valid client certificate, then rebinds the domain to an internal or attacker-controlled IP. The browser continues to use the cached mTLS session and sends the request to the new IP, potentially reaching an internal service that the API exposes but should not be publicly accessible.
Grape itself does not inherently protect against DNS Rebinding; the framework relies on the underlying Rack server and network configuration. If the Grape application uses mTLS primarily for authentication and does not enforce strict host or origin validation on a per-request basis, the combination creates a path where an authenticated client can pivot to internal endpoints. For example, an API might accept requests to api.example.com with a valid client cert, but if the DNS record for api.example.com is rebinded to 127.0.0.1, the request may reach internal services not intended for external access.
The risk is particularly relevant when Grape APIs are deployed in environments where mTLS is used for strong client authentication but host-based access controls are weak or misconfigured. The attack leverages the trust placed in the TLS session and the browser’s same-origin policy relaxation during the rebinding window. MiddleBrick’s scans detect such misconfigurations by testing unauthenticated attack surfaces and cross-referencing OpenAPI specs with runtime behavior, highlighting cases where authentication does not sufficiently constrain network reachability.
To illustrate, consider a Grape endpoint defined as:
class ApiV1 < Grape::API
format :json
before do
# mTLS client cert validation assumed to happen at Rack level
# No additional host or origin check here
end
get :data do
{ message: 'Sensitive data' }
end
end
If the DNS for the domain hosting this API is rebinded, and the underlying server routes the request correctly, an attacker may access /data from an unintended host. MiddleBrick’s checks for BOLA/IDOR and Property Authorization can surface such issues by evaluating whether access controls are bound to the correct network and authentication context.
Mutual Tls-Specific Remediation in Grape — concrete code fixes
Remediation focuses on ensuring that mTLS in Grape is combined with explicit host validation, strict request origin checks, and proper routing constraints. The following code examples demonstrate how to harden a Grape API against DNS Rebinding when using Mutual TLS.
1. Enforce Host Header Validation
Ensure that each request validates the Host header against an allowlist. This prevents requests rebinded to internal or unexpected hosts from being processed.
class ApiV1 < Grape::API
format :json
before do
allowed_hosts = ['api.example.com', 'staging-api.example.com']
unless allowed_hosts.include?(request.host.downcase)
error!('Forbidden host', 403)
end
end
get :data do
{ message: 'Secure data' }
end
end
2. Validate Origin for Browser-Facing Endpoints
If the API serves requests from JavaScript, validate the Origin or Referer headers to ensure they match expected origins. This complements mTLS by adding a second layer against cross-origin rebinding.
class ApiV1 < Grape::API
format :json
before do
allowed_origins = ['https://example.com', 'https://app.example.com']
origin = request.env['HTTP_ORIGIN']
unless allowed_origins.include?(origin)
error!('Invalid origin', 403)
end
end
get :public_data do
{ public: true }
end
end
3. Combine mTLS with Rack-Level Host Constraints
When terminating TLS at a Rack server (e.g., Puma behind a reverse proxy), configure the server to reject requests with mismatched Server Name Indication (SNI) or Host headers. The following example shows a Puma configuration snippet that works alongside Grape to enforce strict host binding.
# config/puma.rb
workers 2
threads 1, 6
preload_app!
on_worker_boot do
# Ensure the server only responds to specific SNI/Host values
Rack::Builder.new do
use Rack::SSL if ENV['RACK_ENV'] == 'production'
run ApiV1
end
end
4. Use Middleware for mTLS and Host Validation
Implement a custom Rack middleware that verifies the client certificate and enforces host rules before the request reaches Grape routes. This keeps validation logic centralized and reusable.
class MtlsHostValidator
def initialize(app)
@app = app
end
def call(env)
request = Rack::Request.new(env)
# Example: check client cert from env['SSL_CLIENT_CERT']
cert = env['SSL_CLIENT_CERT']
unless cert&.subject&;to_s&.include?('CN=allowed-client')
return [403, { 'Content-Type' => 'application/json' }, ['{"error":"client cert invalid"}']]
end
# Host validation
allowed_hosts = ['api.example.com']
unless allowed_hosts.include?(request.host.downcase)
return [403, { 'Content-Type' => 'application/json' }, ['{"error":"host forbidden"}']]
end
@app.call(env)
end
end
# In config.ru
use MtlsHostValidator
run ApiV1
These fixes ensure that even if DNS Rebinding changes the IP address, the request is still validated against strict host and origin policies. MiddleBrick’s scans can verify that such controls are present and correctly configured, reducing the attack surface for APIs using mTLS.