HIGH ssrf server sidegrapehmac signatures

Ssrf Server Side in Grape with Hmac Signatures

Ssrf Server Side in Grape with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Server-side request forgery (SSRF) in a Grape API can occur when an endpoint dynamically builds URLs using attacker-controlled input and then makes outbound HTTP requests. If that request flow includes HMAC-based authentication for an upstream service, the misuse or misunderstanding of HMAC signatures can inadvertently facilitate SSRF or make it harder to detect. For example, an endpoint might accept a target URL and a secret, compute an HMAC over the URL, and forward both to a downstream service that validates the signature before processing the request. An attacker can supply a URL pointing to an internal metadata service (e.g., http://169.254.169.254), while the HMAC they provide matches because the secret is embedded in server-side code. The server trusts the signature and makes the request, effectively acting as a proxy to internal resources. Because HMACs only guarantee integrity and authenticity between parties, they do not enforce network-level restrictions. If the server does not validate the destination, enforce a strict allowlist, or prevent internal IP ranges, the HMAC-enabled flow becomes a trusted channel for SSRF. Additionally, logging or error messages that include the signed payload may leak the HMAC or internal URLs, aiding further reconnaissance. The interaction of Grape routing, parameter parsing, and HMAC verification can therefore create a scenario where an authenticated-looking request triggers an internal SSRF, especially when signature validation is implemented without complementary network controls.

Hmac Signatures-Specific Remediation in Grape — concrete code fixes

Remediation centers on strict input validation, network-level restrictions, and careful handling of HMAC verification in Grape endpoints. Do not rely on HMACs alone to prevent SSRF; treat them as integrity checks, not access controls. Always validate and sanitize user-supplied URLs against a strict allowlist of permitted hosts and ports. Reject URLs with private IP ranges, localhost, or common cloud metadata addresses. Prefer using an allowlist of known, safe domains. Avoid dynamically constructing target URLs from user input. When you must sign requests with HMAC, ensure the data used to compute the signature is canonical and does not include mutable or user-controlled components that could be abused to redirect the request internally.

Example: Secure HMAC verification in Grape

require 'openssl'
require 'base64'
require 'json'

class SecureEndpoint < Grape::API
  format :json

  helpers do
    def valid_hmac?(payload, received_hmac, secret)
      expected_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, payload)
      secure_compare(expected_hmac, received_hmac)
    end

    def secure_compare(a, b)
      return false unless a.bytesize == b.bytesize
      l = a.unpack 'C*'
      res = 0
      b.each_byte { |byte| res |= byte ^ l.shift }
      res == 0
    end

    def allowed_host?(uri)
      allowed = %w[api.example.com service.example.com]
      allowed.include?(uri.host) && !%w[localhost 127.0.0.1 169.254.169.254].include?(uri.host)
    end
  end

  post '/process' do
    payload = params[:payload]
    hmac    = params[:hmac]
    secret  = ENV['HMAC_SECRET']

    # Validate presence
    error!('Missing parameters', 400) unless payload && hmac

    # Parse and validate destination
    uri = URI.parse(payload)
    error!('Invalid URL', 400) unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
    error!('Disallowed host', 403) unless allowed_host?(uri)

    # Verify HMAC integrity
    error!('Invalid signature', 401) unless valid_hmac?(payload, hmac, secret)

    # Safe to forward (with additional safeguards in your HTTP client)
    # Example using net/http (not shown) with timeouts and no proxy for internal addresses
    # ensure outbound request does not follow redirects to internal IPs

    { status: 'ok', host: uri.host }
  end
end

In this example, the HMAC is computed over the canonical payload (e.g., the raw URL string) using a server-side secret. The endpoint parses the payload to enforce host allowlisting and rejects private or metadata IPs before any HMAC verification or outbound request. The secure_compare method prevents timing attacks on HMAC validation. Even when HMAC signatures are used, SSRF risks are mitigated by network controls and strict URL validation. Complementary practices include disabling redirect following or ensuring the HTTP client enforces a strict no-proxy policy for internal addresses. MiddleBrick can help verify that such controls are effective by scanning your Grape API and mapping findings to frameworks like OWASP API Top 10 and PCI-DSS.

Frequently Asked Questions

Can HMAC signatures prevent SSRF in Grape APIs?
No. HMACs provide integrity and authenticity for the signed data, but they do not enforce network-level policies. Without strict host allowlisting and input validation, an attacker can supply a malicious internal URL that the server will request if the HMAC is valid.
What is a safer alternative to signing user-supplied URLs with HMAC in Grape?
Avoid signing and forwarding user-supplied URLs. Instead, use an allowlist of predefined endpoints on the server side, map user actions to internal service calls server-side, and keep HMAC secrets and destinations non-user-controlled. If you must sign, sign only server-generated canonical requests and never include user-controlled paths in the signed data.