Ssrf Server Side in Grape with Basic Auth
Ssrf Server Side in Grape with Basic Auth — how this specific combination creates or exposes the vulnerability
Server Side Request Forgery (SSRF) in a Grape API combined with Basic Authentication can amplify risk by allowing an authenticated context to reach internal services that would otherwise be unreachable. Grape is a Ruby web framework that lets you define API endpoints and authentication hooks. When you add Basic Auth via before hooks, the endpoint expects an Authorization header, but if the downstream HTTP client is not tightly constrained, an attacker can supply a malicious URL that causes the server to open connections to internal addresses (e.g., 127.0.0.1, 169.254.169.254, or internal Kubernetes services).
Basic Auth itself does not cause SSRF, but it changes the exposure surface. An endpoint that validates presence of credentials can still forward requests to arbitrary hosts, and internal services often trust requests coming with service account credentials. For example, a health-check or metadata endpoint that uses Net::HTTP with user-supplied input can be abused to enumerate internal infrastructure. This is especially dangerous when the endpoint performs outbound requests without validating the target host against a denylist or enforcing a strict allowlist of domains.
In a Grape API, if you parse params and pass them directly to an HTTP client without restricting the host, port, or scheme, SSRF becomes feasible even when Basic Auth is enforced. Attack patterns include probing internal metadata services, connecting to Redis or Elasticsearch on localhost, or abusing internal AWS instance metadata. Because the scan tests unauthenticated attack surfaces, middleBrick flags SSRF even when Basic Auth headers are present, since the vulnerability lies in URL handling, not credential validation.
Consider the following vulnerable Grape endpoint that demonstrates the issue:
require 'grape'
require 'net/http'
require 'uri'
class V1 < Grape::API
format :json
before do
authenticate!
end
helpers do
def authenticate!
env['HTTP_AUTHORIZATION'] || error!('Unauthorized', 401)
end
end
get :fetch do
uri = URI(params[:url])
response = Net::HTTP.get_response(uri)
{ status: response.code, body: response.body }
end
end
In this example, a request with a valid Basic Auth header but a malicious value for params[:url] (such as http://169.254.169.254/latest/meta-data/) can lead to SSRF. middleBrick detects such patterns during runtime testing and highlights SSRF alongside the authentication context, enabling you to correlate findings with the presence of Basic Auth.
Basic Auth-Specific Remediation in Grape — concrete code fixes
To mitigate SSRF in Grape endpoints that use Basic Auth, you must validate and sanitize the target URL before making outbound requests. Combine host allowlisting, port restrictions, and URI normalization to reduce risk. Do not rely solely on authentication to protect against SSRF.
Below is a secure Grape endpoint that demonstrates safe practices:
require 'grape'
require 'net/http'
require 'uri'
class SecureApi < Grape::API
format :json
ALLOWED_HOSTS = %w[api.example.com internal.service.local].freeze
ALLOWED_PORTS = [443, 80].freeze
before do
authenticate!
end
helpers do
def authenticate!
# Basic Auth validation using credentials comparison
auth = env['HTTP_AUTHORIZATION']
return unless auth&.start_with?('Basic ')
decoded = Base64.strict_decode64(auth.sub('Basic ', ''))
# In production, compare against secure credential store
halt 401, { error: 'Invalid credentials' }.to_json unless valid_credentials?(decoded)
end
def valid_credentials?(cred)
# Example: compare against environment variables
user, pass = cred.split(':', 2)
user == ENV['API_USER'] && pass == ENV['API_PASS']
end
def safe_fetch(url_str)
uri = URI.parse(url_str)
unless ALLOWED_HOSTS.include?(uri.host)
raise ArgumentError, 'Host not allowed'
end
unless ALLOWED_PORTS.include?(uri.port)
raise ArgumentError, 'Port not allowed'
end
uri = URI::HTTPS === uri ? uri : uri.dup.tap { |u| u.scheme = 'https' }
response = Net::HTTP.get_response(uri)
{ status: response.code, body: response.body }
rescue URI::InvalidURIError, ArgumentError => e
{ error: e.message }
end
end
get :fetch do
result = safe_fetch(params[:url])
present result
end
end
This approach restricts outbound requests to known hosts and ports, enforces HTTPS, and avoids passing raw user input to the HTTP client. middleBrick recognizes such controls during scans and reports reduced risk when host and port validation are in place.
Additionally, consider using a vetted HTTP client library that supports proxying and connection pooling, and avoid following redirects to internal destinations. Regularly review logs for unexpected outbound connections and integrate middleBrick into your CI/CD pipeline to catch regressions early.