HIGH ssrf server sidegrapemutual tls

Ssrf Server Side in Grape with Mutual Tls

Ssrf Server Side in Grape with Mutual Tls

Server-side SSRF in a Grape API can occur when user-controlled input is used to drive HTTP requests to arbitrary hosts. In a setup that also enforces mutual TLS (mTLS), the server presents a client certificate when initiating outbound requests. An attacker can supply a URL that points to an internal service (for example, http://169.254.169.254/latest/meta-data/ in cloud environments or http://internal-database:8080/), and the server’s outbound mTLS handshake may succeed if the client certificate is valid, allowing the server to reach destinations not intended to be publicly reachable.

The presence of mTLS does not inherently prevent SSRF; it changes the trust boundary. If the server’s certificate is accepted by internal services and the server code does not validate and restrict target hosts, the attacker can pivot to internal infrastructure. In a Grape-based Ruby service, this commonly manifests in an endpoint that calls Net::HTTP or a Faraday-based client where the URL is taken directly from params without strict allowlisting or host normalization. The mTLS client certificate is automatically used by the HTTP client if configured in the request setup, which means an SSRF payload to an internal endpoint can complete a handshake and return sensitive metadata or service responses.

Consider a Grape resource that fetches a remote URL provided by the user:

class V1:: ProxyResource < Grape::API
  format :json
  before { header['Content-Type'] = 'application/json' }

  get :fetch do
    url = params[:url] # user-controlled
    response = Net::HTTP.get_response(URI(url))
    { status: response.code, body: response.body }
  end
end

If the server’s HTTP client is configured with an mTLS client certificate (e.g., via a custom Net::HTTP.start block that sets cert and key), an SSRF payload such as http://127.0.0.1:9000/ or http://metadata.internal/ will succeed if those internal endpoints accept the client certificate. This effectively bypasses network-level segregation, exposing internal services through the outward-facing API.

To map findings to public references, an SSRF issue discovered in this context may correspond to CWE-918 and aligns with the OWASP API Top 10 under broken object level authorization or excessive data exposure patterns when internal data is returned. Unlike some scanners that only test for classic external SSRF, middleBrick’s checks include runtime analysis that can surface SSRF combined with mTLS-enabled outbound calls, providing a more complete picture of the unauthenticated attack surface.

Mutual Tls-Specific Remediation in Grape

Remediation focuses on input validation, network segregation, and safe HTTP client usage. Do not rely on mTLS to restrict destinations; treat the client certificate as an identity mechanism for the server when it acts as a client, not as a boundary control for where the client may connect.

First, avoid forwarding user input directly into HTTP requests. Use an allowlist of permitted hosts and ports, and normalize the URL to prevent bypass via redirects, encoding, or alternate URI representations. Second, configure the HTTP client to restrict which hosts it will connect to, and avoid using the same client certificate for calls that target user-supplied hosts. Third, prefer higher-level HTTP clients that support strict configuration, and ensure timeouts and redirect policies are hardened.

Example of a safer Grape endpoint with host validation and a dedicated, restricted HTTP client:

require 'uri'

ALLOWED_HOSTS = ['api.example.com', 'data.example.com'].freeze

def safe_http_client
  @safe_http_client ||= begin
    client = Net::HTTP.new('api.example.com', 443)
    client.use_ssl = true
    client.cert = OpenSSL::X509::Certificate.new(File.read('client.crt'))
    client.key = OpenSSL::PKey::RSA.new(File.read('client.key'))
    client.verify_mode = OpenSSL::SSL::VERIFY_PEER
    client.ca_file = 'ca_bundle.pem'
    client
  end
end

class V1:: FetchResource < Grape::API
  format :json

  get :fetch do
    uri = URI.parse(params[:url])
    unless ALLOWED_HOSTS.include?(uri.host)
      throw(:error, { message: 'host not allowed', status: 400 })
    end

    request = Net::HTTP::Get.new(uri.request_uri)
    response = safe_http_client.request(request)
    { status: response.code, body: response.body }
  rescue URI::InvalidURIError, SocketError, Net::HTTPBadResponse
    error!({ message: 'invalid or unreachable target' }, 400)
  end
end

In the example above, the mTLS client certificate is used only for calls to a fixed, preapproved host. The client does not dynamically adopt the certificate for arbitrary outbound connections initiated via user input. This pattern prevents an attacker from leveraging the API to reach internal services or metadata endpoints even if the server’s certificate is trusted by those internal services.

For automation and CI/CD, middleBrick’s Pro plan can add continuous monitoring and GitHub Action integration to ensure that any API changes do not introduce new SSRF risks or weaken host validation. When a scan detects SSRF findings, you can fail builds or route alerts to Slack or Teams, keeping security checks tightly coupled with deployment pipelines.

Frequently Asked Questions

Does mutual TLS prevent SSRF in Grape APIs?
No. Mutual TLS provides client authentication for outbound connections but does not restrict where the server can connect. Without host validation and safe request routing, SSRF can still occur.
How does middleBrick handle SSRF combined with mTLS configurations?
middleBrick scans the unauthenticated attack surface and can detect SSRF patterns even when mTLS is in use, focusing on runtime behavior and whether internal hosts are reachable through user-controlled inputs.