HIGH cross site request forgerygrapemutual tls

Cross Site Request Forgery in Grape with Mutual Tls

Cross Site Request Forgery in Grape with Mutual Tls

Cross-site request forgery (CSRF) in a Grape API with mutual TLS (mTLS) involves a subtle interaction between transport-layer client authentication and application-level authorization. mTLS ensures that only clients with a valid certificate can establish a TLS connection and reach your endpoints. This is a strong safeguard against network-level request injection, but it does not automatically protect against CSRF within the authenticated session.

CSRF relies on the victim’s existing authenticated session—typically via cookies—to trigger unwanted actions. With mTLS, the connection itself is authenticated, which prevents an attacker from establishing a new TLS session without a valid client certificate. However, once a client with a valid certificate has an active session (for example, using session cookies or bearer tokens issued after mTLS authentication), a forged request from a malicious site can still be executed by the client if the browser automatically includes credentials such as cookies or authorization headers.

Consider a Grape endpoint that performs sensitive actions via simple GET requests or relies on cookies for session management after mTLS authentication:

# config.ru or a mounted API class
require 'grape'
require 'openssl'

If the API does not implement anti-CSRF tokens or require explicit consent for state-changing methods, an attacker can craft a request that leverages the victim’s browser session. Even with mTLS ensuring the client is who they claim to be at the TLS layer, the API may still treat the request as legitimate because it arrives with valid client-side credentials.

The risk is particularly pronounced when mTLS is used for initial client authentication but the API subsequently uses cookie-based sessions or does not validate the Origin / Referer headers. An attacker can host a malicious page that triggers requests to the Grape API, and the browser will automatically include the session cookies or client certificate context, leading to unauthorized operations such as changing email, modifying permissions, or initiating transfers.

To contextualize, this aligns with OWASP API Top 10 API5:2023 — Broken Function Level Authorization, where lack of proper authorization checks on state-changing requests enables CSRF-like abuse even when transport-layer authentication is strong.

Mutual Tls-Specific Remediation in Grape

Remediation focuses on ensuring that mTLS is complemented by explicit CSRF protections at the API layer. Relying solely on mTLS is insufficient; you must enforce anti-CSRF measures for state-changing operations and validate request origins.

1. Use anti-CSRF tokens for state-changing actions

Require a one-time token (e.g., synchronizer token pattern) for POST, PUT, PATCH, and DELETE requests. The token must be unpredictable and tied to the user/session context, not derivable from the certificate alone.

# Example Grape endpoint requiring CSRF token in header
class ProtectedAPI < Grape::API
  format :json

2. Validate Origin and Referer headers

Explicitly check the Origin or Referer header to ensure requests originate from your trusted frontend domain. This is not foolproof (headers can be omitted), but it provides an additional layer alongside mTLS.

before do
  allowed_origin = 'https://your-trusted-app.com'
  origin = env['HTTP_ORIGIN']
  referer = env['HTTP_REFERER']
  unless origin == allowed_origin || referer&.start_with?(allowed_origin)
    error!('Forbidden', 403)
  end
end

3. Require custom headers for API requests

Configure your frontend to send a custom header (e.g., X-Requested-With: XMLHttpRequest) for all API calls. Browsers restrict cross-origin requests from executing if this header is missing, mitigating simple CSRF attacks.

before do
  halt 403, { error: 'Missing CSRF header' }.to_json unless env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
end

4. Use same-site cookies and secure cookie attributes

If your API relies on cookies, set SameSite=Strict or Lax and ensure Secure and HttpOnly flags are enabled. This reduces the browser’s willingness to include cookies in cross-origin requests.

# Example in a Ruby web server (e.g., Puma/rack) configuration
# config.ru: use Rack::Session::Cookie, same_site: :strict, secure: true, httponly: true

5. Enforce mTLS correctly and avoid mixing authentication modes

Ensure that mTLS client certificates are required for all endpoints and that no fallback to unauthenticated access exists. Combine mTLS with per-request authorization checks rather than assuming session-level trust.

# Grape example enforcing client certificate validation
class SecureAPI < Grape::API
  helpers do
Remediation StrategyWhy it matters with mTLSImplementation priority
CSRF tokens for state-changing methodsPrevents forged requests even when mTLS authenticated session existsHigh
Origin/Referer validationAdds layer to detect cross-origin abuseMedium
Custom request headersBlocks simple cross-origin POSTs from browsersMedium
Same-site cookiesReduces cookie leakage to third-party contextsLow

Frequently Asked Questions

Does mTLS alone prevent CSRF in Grape APIs?
No. mTLS authenticates the client at the transport layer, but once a session is established (e.g., via cookies or tokens), CSRF can still occur if the API does not validate request origins or use anti-CSRF tokens.
Can I rely on the same Origin header for CSRF protection in production?
No. The Origin and Referer headers can be omitted by some clients or proxies. Treat them as a helpful additional check, not a primary defense; combine with anti-CSRF tokens and secure cookie attributes.