Ssrf Server Side in Grape (Ruby)
Ssrf Server Side in Grape with Ruby — how this specific combination creates or exposes the vulnerability
Server Side Request Forgery (SSRF) in a Grape API built with Ruby occurs when an attacker can coerce the server into making arbitrary outbound HTTP requests from the host running the Ruby process. Because Grape encourages building rich endpoints with flexible parameter handling, it is common to forward query values into HTTP clients without strict validation. In Ruby, common libraries such as Net::HTTP, HTTParty, or Faraday are frequently used inside resource actions. If user input (e.g., a URL or host parameter) is passed directly to these clients, an attacker can reach internal metadata services, file:// or gopher:// endpoints, or other internal listeners that are not exposed externally.
Grape resources typically define routes and helpers that make it easy to extract params and perform requests inline. For example, a developer might write a proxy or webhook integration that accepts a target URL from the client. Without a strict allowlist or hostname validation, an attacker can supply internal IPs (169.254.169.254 for AWS metadata), localhost services, or docker internal DNS names to probe the runtime environment. The Ruby runtime does not inherently prevent these internal connections, so SSRF becomes a practical risk when input is trusted.
Additionally, Ruby on Rails or other frameworks integrated with Grape may introduce further SSRF vectors via ActiveJob, background workers, or service objects that reuse the same HTTP client logic. MiddleBrick’s checks for SSRF and related patterns help surface these risks by correlating OpenAPI specifications with runtime behavior, ensuring that endpoints accepting free-form URLs are flagged for review. Understanding how Grape routes, parameter coercion, and Ruby HTTP clients interact is essential to designing controls that prevent SSRF in production.
Ruby-Specific Remediation in Grape — concrete code fixes
Remediation focuses on strict input validation, allowlisting, and avoiding direct use of user-controlled data in HTTP requests. In Grape, define strong parameter rules and validate hostnames or IPs before any network call. Below are concrete, safe patterns for common Ruby HTTP clients used in Grape resources.
1) Use a strict hostname allowlist and reject private/reserved IPs
require 'grape'
require 'net/http'
require 'uri'
class ProxyResource < Grape::API
ALLOWED_HOSTS = %w[api.example.com files.example.com]
helpers do
def safe_uri(input)
uri = URI.parse(input)
unless uri.respond_to?(:host) && ALLOWED_HOSTS.include?(uri.host)
fail ArgumentError, 'Host not allowed'
end
# Ensure no embedded credentials or alternate ports that change behavior
uri.port = 443 if uri.scheme == 'https'
uri
rescue URI::InvalidURIError
fail ArgumentError, 'Invalid URI'
end
end
get 'fetch' do
target = safe_uri(params[:url])
response = Net::HTTP.get_response(target)
{ status: response.code, body: response.body }
end
endThis pattern validates the hostname against an allowlist and normalizes the port, reducing SSRF risk while still supporting legitimate use cases.
2) Use a whitelisted set of schemes and disable dangerous protocols
require 'grape'
require 'net/http'
require 'uri'
class SafeProxyResource < Grape::API
ALLOWED_SCHEMES = %w[https http]
helpers do
def sanitize_url(input)
uri = URI.parse(input)
unless ALLOWED_SCHEMES.include?(uri.scheme)
fail ArgumentError, 'Scheme not allowed'
end
# Block internal IPs and reserved ranges
ip = Resolv.getaddress(uri.host) rescue nil
if ip && IPAddr.new('10.0.0.0/8').include?(IPAddr.new(ip))
fail ArgumentError, 'Private IP not allowed'
end
uri
rescue Resolv::ResolvError, URI::InvalidURIError, ArgumentError
fail ArgumentError, 'Invalid or unsafe target'
end
end
get 'fetch-safe' do
target = sanitize_url(params[:url])
response = Net::HTTP.get_response(target)
{ status: response.code, headers: response.to_hash, body: response.body }
end
end3) If using Faraday, avoid passing raw user input to the connection URL and prefer adapter-level timeouts and a strict proxy bypass for internal addresses
require 'grape'
require 'faraday'
class FaradayResource < Grape::API
ALLOWED_HOSTS = %w[api.example.com]
get 'fetch-faraday' do
target_host = URI(params[:url]).host
unless ALLOWED_HOSTS.include?(target_host)
fail ArgumentError, 'Host not allowed'
end
conn = Faraday.new(url: params[:url]) do |faraday|
faraday.request :url_encoded
faraday.response :logger
faraday.adapter Faraday.default_adapter
end
response = conn.get
{ status: response.status, body: response.body }
end
endThese examples emphasize validation at the resource layer, avoiding blind concatenation of user input, and using Ruby standard libraries safely. Combined with MiddleBrick’s scans for SSRF and related findings, these practices reduce the likelihood of unintended internal network exposure in Grape-based Ruby APIs.