Cross Site Request Forgery in Grape with Bearer Tokens
Cross Site Request Forgery in Grape with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) is an attack that tricks a victim into executing unwanted actions on a web application in which they are authenticated. When APIs use Bearer Tokens for authentication, developers may assume that because tokens are required, requests are automatically protected against CSRF. This assumption is incorrect in certain deployment and client contexts.
Grape is a REST-like API micro-framework for Ruby, often used with Rack. APIs built with Grape commonly accept Bearer Tokens via the Authorization header. While sending credentials in headers helps avoid some traditional cookie-based CSRF issues, the vulnerability can still arise when requests are made from browsers in specific scenarios:
- Authenticated browsers automatically include cookies and HTTP authentication credentials for same-site requests. If your Grape API is called from browser JavaScript (e.g., via
fetchorXMLHttpRequest) and relies only on Bearer Tokens in headers, an attacker can craft a form or image request that triggers state-changing methods (POST, PUT, DELETE). Browsers will include session-related cookies if present, and if the Grape app also accepts cookie-based sessions or if the token is stored in a way the browser can auto-send (rare but possible), the request may be executed unintentionally. - CSRF risk increases when the API serves responses to browser-origin requests (e.g., JavaScript clients) and lacks explicit anti-CSRF mechanisms. Even with Bearer Tokens, if the token is stored in localStorage and exposed via XSS, the attacker can read it and forge requests with the correct Authorization header; however, a forged request from a third-party site won’t automatically include the token unless the client-side code explicitly adds it. The danger arises when the API does not validate the Origin header or use SameSite cookie attributes for any auxiliary cookies.
- Non-browser clients (like mobile apps or curl) are not susceptible to CSRF because they do not automatically attach credentials across origins. However, browser-based clients consuming a Grape API must be treated with caution. Attack vectors such as malicious sites embedding forms or scripts that call your API endpoints can succeed if defenses are weak.
In practice, a Grape endpoint that accepts Bearer Tokens but does not implement CSRF-specific protections can be exploited in a browser context when cross-origin requests are allowed via CORS misconfigurations or when legacy cookie-based authentication is also supported. For example, a DELETE /accounts/1 endpoint protected by a Bearer Token can be invoked via a simple form submission if the browser’s credential storage behavior inadvertently permits it, or if the token is inadvertently exposed to the browser environment.
Bearer Tokens-Specific Remediation in Grape — concrete code fixes
To mitigate CSRF for Grape APIs using Bearer Tokens, focus on strict request validation and browser-specific protections. Below are concrete, actionable fixes with code examples.
1. Enforce Origin and Referer Validation
Explicitly check the Origin and Referer headers for known, trusted origins. Reject requests with missing or unexpected values.
# In your Grape API class or a before block
before do
allowed_origins = ['https://app.yourdomain.com', 'https://dashboard.yourdomain.com']
origin = request.env['HTTP_ORIGIN']
referer = request.env['HTTP_REFERER']
unless allowed_origins.include?(origin) || allowed_origins.include?(referer)
error!('Forbidden', 403)
end
end
2. Use SameSite Cookie Attributes for Any Session Cookies
If your Grape app sets cookies (e.g., for a web frontend), ensure SameSite is set to Lax or Strict. This prevents browsers from sending cookies in cross-site requests.
# Example when setting a cookie in a Rack-compatible response
set_cookie 'session_id', {
value: 'abc123',
httponly: true,
secure: true,
same_site: :lax
}
3. Require Anti-CSRF Tokens for State-Changing Methods
For browser-origin requests, require a custom header (e.g., X-CSRF-Token) that cannot be set cross-origin due to CORS restrictions. This pattern is effective when your frontend reads a token and includes it in headers.
# Grape endpoint expecting CSRF token for destructive methods
class MyAPI < Grape::API
before do
if %w[POST PUT DELETE PATCH].include?(request.request_method)
provided_token = request.env['HTTP_X_CSRF_TOKEN']
expected_token = env['rack.session']&.[]('csrf_token')
unless provided_token&;casecmp(expected_token)
error!('Invalid CSRF token', 422)
end
end
end
desc 'Delete an account', csrf_protected: true
params do
requires :account_id, type: Integer
end
delete '/accounts/:account_id' do
# deletion logic
end
end
4. Configure CORS Strictly
Limit which origins can interact with your API. Avoid wildcard origins when state-changing operations are involved.
# Using a Rack CORS middleware initializer (config/initializers/cors.rb)
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'https://app.yourdomain.com'
resource '*',
headers: :any,
methods: [:get, :post, :put, :delete],
expose: ['X-CSRF-Token'],
max_age: 86400
end
end
5. Avoid Relying Solely on Bearer Tokens in Browser Contexts
Do not store Bearer Tokens in localStorage if your API is called from browsers, as XSS can lead to token theft. Prefer short-lived tokens stored in memory or secure, HttpOnly cookies where appropriate. For SPAs, use the Authorization header with tokens obtained via secure flows and ensure strict CORS rules.
By combining these measures—origin validation, SameSite cookies, CSRF tokens for mutating requests, and strict CORS—you significantly reduce the CSRF risk for Grape APIs using Bearer Tokens, even when accessed from browser-based clients.