Open Redirect in Grape with Hmac Signatures
Open Redirect in Grape with Hmac Signatures — how this specific combination creates or exposes the vulnerability
An open redirect in a Grape API occurs when an endpoint uses an attacker-controlled URL parameter to perform an HTTP redirect without strict validation. When Hmac Signatures are used to authenticate requests, developers sometimes assume the signature validates the entire request, including the target redirect location. This assumption can create a false sense of security: the Hmac may verify that the request was not tampered with in transit, but it does not automatically validate that the redirect target is safe.
Consider a Grape endpoint that accepts a redirect_url parameter and a separate signature parameter. The server verifies the Hmac to ensure integrity, then performs a redirect using the provided URL. Because the signature does not bind to the semantic meaning of redirect_url, an attacker can supply a malicious external host in the parameter while the signature remains valid. The signature may have been computed over the raw query string, which includes the attacker-supplied URL, but the server still chooses to follow that URL in a redirect response. This results in an open redirect that is reachable only when a valid Hmac is provided, potentially making the link appear legitimate to users or automated systems.
In practice, this can be chained with social engineering: an attacker sends a victim a link to a trusted domain that includes a valid Hmac, leading the victim to a malicious site. Because the Hmac is verified before the redirect, the server processes the request as expected from an integrity standpoint, but the business logic fails to enforce a safe set of destinations. The vulnerability is not in the Hmac algorithm itself, but in the lack of allowlisting or strict validation of the redirect target after integrity checks pass.
An attacker may also probe whether the Hmac covers the redirect parameter by manipulating the signature parameter and observing whether the server rejects the request. If the server recomputes the Hmac over the entire set of parameters including redirect_url, a missing or weak canonicalization scheme may still allow a valid signature with a dangerous destination. This is especially relevant when the API uses query parameters for both routing and redirection, and the developer does not treat the redirect target as user-controlled input requiring strict validation.
Hmac Signatures-Specific Remediation in Grape — concrete code fixes
To remediate open redirect when using Hmac Signatures in Grape, treat the redirect target as untrusted input regardless of signature validity. Always validate the destination against an allowlist of trusted hosts or use a relative path redirect when possible. Do not directly redirect to user-supplied URLs, even if a valid Hmac is present.
Below is a secure Grape example that validates the redirect target before issuing a response. The Hmac is verified first, then the target URL is checked against an allowlist and normalized to prevent open redirect bypasses via URL encoding or mixed case schemes.
require 'openssl'
require 'rack'
class SecureApi < Grape::API
format :json
helpers do
def verified_redirect_url(input_url)
# Only allow relative paths or a strict allowlist of hosts
allowed_hosts = ['app.example.com', 'static.example.com']
uri = URI.parse(input_url)
if uri.relative?
input_url
else
host = uri.host
if allowed_hosts.include?(host)
# Ensure the scheme is explicitly https for external hosts
"https://#{host}#{uri.path}#{uri.query ? \"?\" + uri.query : ''}#{uri.fragment ? \"#\" + uri.fragment : ''}"
else
nil
end
end
rescue URI::InvalidURIError
nil
end
def compute_hmac(payload, secret)
OpenSSL::HMAC.hexdigest('sha256', secret, payload)
end
end
before do
# Example: verify Hmac over selected parameters, excluding the redirect destination
secret = ENV['HMAC_SECRET']
received_signature = params['signature']
# Exclude signature and redirect_url from the signed payload
payload_parts = params.except('signature', 'redirect_url').sort.map { |k, v| "#{k}=#{v}" }.join('&')
expected_signature = compute_hmac(payload_parts, secret)
halt 401, { error: 'invalid_signature' }.to_json unless Rack::secure_compare(received_signature.to_s, expected_signature)
end
get '/external' do
target = params['redirect_url']
safe_url = verified_redirect_url(target)
halt 400, { error: 'invalid_redirect' } unless safe_url
{ redirect_url: safe_url }
end
end
In this example, the Hmac is computed over all parameters except redirect_url and signature. This prevents an attacker from altering the redirect destination and expecting the signature to remain valid. After verification, the endpoint validates the target using verified_redirect_url, which allows only relative paths or pre-approved hosts with an enforced HTTPS scheme for external destinations. This approach separates integrity verification from destination validation, mitigating the open redirect while preserving legitimate use cases.
FAQ
- q: Can an Hmac prevent open redirect if it covers the redirect_url parameter?
- a: No. Including the redirect URL in the Hmac ensures integrity, but it does not validate safety. You must still enforce allowlisting or relative paths; otherwise a valid signature can accompany a malicious destination.
- q: Should I remove Hmac Signatures to avoid misleading security?
- a: No. Use Hmac Signatures to verify request integrity, but treat redirect targets as untrusted input and validate them independently with allowlists or strict rules.