Dns Rebinding in Grape (Ruby)
Dns Rebinding in Grape with Ruby — how this specific combination creates or exposes the vulnerability
DNS Rebinding is a client-side attack where a malicious webpage resolves a hostname to an internal IP address (e.g., 127.0.0.1), then quickly re-resolves the same hostname to a different IP, bypassing same-origin protections. When you build APIs with the Grape framework in Ruby, DNS Rebinding can expose internal services or bypass network-based access controls if the server trusts the Host header or uses request metadata for routing decisions.
Grape is a REST-like API framework that sits on Rack. In Ruby, the effective target for rebinding is often a Rack-based endpoint served by Grape, because the application may inspect request.host, request.headers, or use the Host header for internal routing or virtual-host logic. An attacker can serve a page that resolves a domain like internal.api.example.com first to 127.0.0.1, making the browser send authenticated requests to your Grape app on localhost. If the Grape app uses hostname-based allowlists or trusts the Host header without validation, the request may be processed as if it originated from an external, trusted source.
Consider a Grape endpoint that performs internal service discovery based on the Host header:
class InternalServiceAPI < Grape::API
format :json
# Risky: using request.host for routing or authorization
route_param :service do
get do
target_host = params[:host] || request.host
# If target_host is attacker-controlled via DNS rebinding,
# internal host resolution may be bypassed.
{ resolved_target: target_host, status: 'ok' }
end
end
end
If an attacker crafts a page that makes requests to https://internal.api.example.com/service/foo?host=127.0.0.1, and the DNS for internal.api.example.com is rebindable, the browser may send the request to 127.0.0.1. The server-side code above would reflect the internal IP or hostname, potentially exposing internal endpoints or allowing SSRF-like behavior when combined with other weaknesses.
Grape apps behind reverse proxies or load balancers may also be vulnerable if the proxy normalizes the Host header and the app uses Ruby’s request object to derive internal decisions. The vulnerability is not in Grape itself but in how the Ruby application uses request metadata without additional validation or network segregation.
To detect DNS Rebinding in the context of Grape and Ruby, middleBrick performs active probes that attempt to infer whether a hostname can resolve to both public and private IPs during a single session. The scanner checks for inconsistencies in the Host header handling and flags endpoints that use untrusted request metadata in security-sensitive decisions.
Ruby-Specific Remediation in Grape — concrete code fixes
Remediation focuses on never trusting the Host header or any client-supplied hostname for routing, authorization, or internal resolution. In Ruby with Grape, validate and normalize host values against an explicit allowlist or use server-side configuration instead of request-derived values.
1. Avoid using request.host for routing or sensitive decisions. Instead, derive behavior from authenticated context or fixed configuration:
class SafeServiceAPI < Grape::API
format :json
# Safe: no reliance on request.host for routing
resource :service do
before do
# Require authentication and use a service identifier that is not derived from Host
error!('Unauthorized', 401) unless authenticated?
end
params do
requires :service_id, type: String, desc: 'Service identifier'
end
get ':service_id' do
service_id = params[:service_id]
# Use server-side mapping or a controlled directory lookup
service = find_service(service_id)
{ service: service.name, status: 'ok' }
end
end
end
2. Validate Host header or use a proxy-mode setting. If you must inspect the host, compare against a strict allowlist and reject unexpected values:
class ValidatedHostAPI < Grape::API
format :json
ALLOWED_HOSTS = ['api.example.com', 'www.example.com'].freeze
before do
def validate_host
unless ALLOWED_HOSTS.include?(request.host)
error!('Forbidden host', 403)
end
end
route_param :service do
get do
# Only proceed if host was validated
{ service: params[:service], host: request.host }
end
end
end
3. Harden Rack configuration and proxy settings. Ensure your Rack server or reverse proxy sets headers like X-Forwarded-Host consistently and that your Ruby app uses a trusted proxy configuration to avoid spoofing:
# config.ru
use Rack::RequestIP
# If behind a trusted proxy, set trusted proxies in your server (e.g., Puma/Thin config)
# and avoid parsing X-Forwarded-Host without strict validation.
run MyGrapeApp
4. Use server-side resolution for internal services. Instead of resolving hostnames on demand, use a service registry or environment variables that are not influenced by client-controlled DNS:
class InternalClient
def self.resolve(name)
# Map service names to fixed, internal endpoints
endpoints = {
'users' => 'http://127.0.0.1:4001',
'orders' => 'http://127.0.0.1:4002'
}
endpoints.fetch(name) { raise 'Unknown service' }
end
end
By combining these practices—removing reliance on request.host, validating against a fixed allowlist, and using server-side service discovery—you reduce the attack surface for DNS Rebinding in Grape APIs built with Ruby.
middleBrick can help verify these fixes by scanning your endpoints and checking for Host header misuse and SSRF-like patterns. If you use continuous scanning with the Starter plan or higher, your Grape endpoints can be monitored on a schedule, and the Pro plan adds CI/CD integration to fail builds if risky patterns are detected.