HIGH cors wildcardgrapehmac signatures

Cors Wildcard in Grape with Hmac Signatures

Cors Wildcard in Grape with Hmac Signatures — how this specific combination creates or exposes the vulnerability

A Access-Control-Allow-Origin: * wildcard combined with HMAC signatures in a Grape API creates a dangerous mismatch between authentication and authorization. HMAC signatures typically rely on a shared secret to sign requests, and many implementations treat the signature as proof of integrity and origin. When allow_origin: '*' is set, the browser will accept responses for any origin, which can cause the signed response to be read by a malicious site if the request also includes credentials or sensitive data. The CORS spec prohibits credentials with a wildcard, but many clients and servers misconfigure this by allowing credentials while using the wildcard, or by not tightening the origin check after signature validation. This enables a scenario where an attacker can trick a victim’s browser into making a signed request (e.g., via an embedded image or script), and the response may be accessible to the attacker if CORS is too permissive, leading to data exposure or unauthorized actions being replayed.

In Grape, a common pattern is to verify the HMAC signature in an API helper, but if the CORS middleware sets allow_origin: '*' before the route runs, the browser may expose the response to a malicious page. Even if the server validates the signature correctly, the client-side JavaScript can read the response because the wildcard allows any origin. This is particularly risky for endpoints that return sensitive information or tokens. An attacker can craft a request that includes the victim’s authentication cookie (if sent automatically) and a valid HMAC (if the secret is leaked or predictable), and the wildcard CORS policy allows the attacker’s page to read the response. The combination therefore turns a correctly signed request into a cross-origin data leak.

Real-world findings from scanners highlight this pattern: endpoints with HMAC verification but a wildcard CORS policy receive a high severity score because the control boundary is effectively bypassed. The signature ensures the request has not been tampered with, but it does not prevent the response from being delivered to an unauthorized origin. This maps to OWASP API Top 10 controls related to broken object level authorization and excessive data exposure, and can intersect with SSRF if the wildcard is combined with unchecked redirect parameters. The risk is not theoretical; tools have demonstrated extraction of signed tokens via embedded malicious pages when CORS is misconfigured.

Hmac Signatures-Specific Remediation in Grape — concrete code fixes

To fix the issue, tighten CORS to specific origins and ensure HMAC verification occurs before any response is sent. Do not use * when credentials or sensitive data are involved. Instead, explicitly set allowed origins and mirror the Origin header when it matches your allowlist. Below is a complete Grape API example with HMAC verification and secure CORS settings.

require 'grape'
require 'openssl'
require 'base64'

class SecureApi < Grape::API
  # Explicit allowlist — never use '*' when handling authenticated/signed responses
  ALLOWED_ORIGINS = ['https://app.example.com', 'https://admin.example.com'].freeze

  before { verify_hmac_signature }
  after { set_secure_cors_headers }

  helpers do
    def verify_hmac_signature
      provided = request.env['HTTP_X_API_SIGNATURE']
      timestamp = request.env['HTTP_X_API_TIMESTAMP']
      return error!('Missing signature', 401) unless provided&.present? && timestamp&.present?

      # Reject stale requests (replay protection)
      within = 300 # 5 minutes
      now = Time.now.to_i
      return error!('Request expired', 401) if (now - timestamp.to_i).abs > within

      body = request.body.read
      request.body.rewind

      secret = ENV.fetch('HMAC_SECRET') { raise 'HMAC secret missing' }
      computed = OpenSSL::HMAC.hexdigest('sha256', secret, [timestamp, body].join('.'))
      return error!('Invalid signature', 401) unless ActiveSupport::SecurityUtils.secure_compare(computed, provided)

      # Optionally set an authenticated request flag for downstream routes
      @api_auth = { verified: true, timestamp: timestamp }
    end

    def set_secure_cors_headers
      origin = request.env['HTTP_ORIGIN']
      if origin && ALLOWED_ORIGINS.include?(origin)
        header['Access-Control-Allow-Origin'] = origin
        header['Access-Control-Allow-Credentials'] = 'true'
      else
        header['Access-Control-Allow-Origin'] = ''
        header['Access-Control-Allow-Credentials'] = 'false'
      end
      header['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
      header['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-API-Signature, X-API-Timestamp'
      header['Access-Control-Expose-Headers'] = 'X-Rate-Limit-Limit, X-Rate-Limit-Remaining'
      header['Access-Control-Max-Age'] = '86400'
    end
  end

  # Example protected endpoint
  get '/resource' do
    # At this point, HMAC is verified and origin is trusted
    { data: 'sensitive', verified_at: @api_auth[:timestamp] }
  end

  # Preflight handling
  options '*' do
    set_secure_cors_headers
    200
  end
end

Key points in the remediation:

  • Replace allow_origin: '*' with an explicit allowlist and mirror the Origin header only when it matches.
  • Always use secure_compare (or equivalent constant-time comparison) to prevent timing attacks on the HMAC.
  • Include a timestamp and short window to prevent replay attacks; reject requests outside the window.
  • Set Access-Control-Allow-Credentials only when origin is trusted, and avoid sending it with a wildcard.
  • Expose only necessary headers and avoid exposing sensitive data to any origin.

For teams using the middleBrick ecosystem, the CLI can be integrated into CI to catch these misconfigurations before deployment: use middlebrick scan <url> to detect wildcard CORS with HMAC setups. If you need continuous oversight, the Pro plan adds scheduled scans and GitHub Action PR gates to fail builds when a high-severity CORS or signature validation issue is found. The Dashboard helps track these findings over time, and the MCP Server lets you scan APIs directly from your AI coding assistant while you develop.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Is it safe to use Access-Control-Allow-Origin: * if I validate HMAC signatures on the server?
No. A wildcard origin allows any website to read the response in the browser when credentials are included or the request is made via CORS. Even with HMAC verification, a malicious page can trigger a signed request and read the response if CORS permits. Use an explicit allowlist and mirror the Origin header instead.
How should I handle preflight requests for an API that uses HMAC signatures?
Respond to OPTIONS requests with the specific allowed origins, methods, and headers. Do not echo a wildcard value for Access-Control-Allow-Origin. Validate that the Origin is in your allowlist and set Access-Control-Allow-Credentials only for trusted origins. Include necessary headers such as Access-Control-Allow-Methods and Access-Control-Allow-Headers, and consider a short max-age for performance.