Vulnerability
Rails

CSRF in Rails Applications

Rails includes built-in CSRF protection via protect_from_forgery and the authenticity_token mechanism. However, API-mode Rails apps disable this by default, and developers sometimes skip CSRF for turbo/hotwire AJAX requests or set protect_from_forgery with: :null_session without understanding the implications.

Scan Your Rails App

How CSRF Manifests in Rails

Rails CSRF protection can be weakened by: - Rails API mode (rails new --api) disables CSRF middleware entirely - Using skip_before_action :verify_authenticity_token on controllers - Setting protect_from_forgery with: :null_session (silently fails instead of raising) - Forgetting csrf_meta_tags in layouts - Turbo/Hotwire requests that do not include the CSRF token

Real-World Impact

A Rails application in API mode served both a React SPA and mobile apps. The SPA used cookie-based session auth without CSRF protection. An attacker created a blog post with an embedded form that transferred subscription credits from the victim's account to the attacker's when any logged-in user viewed the post.

Step-by-Step Fix

1

Enable CSRF in ApplicationController

Ensure protect_from_forgery is configured for all web controllers.

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end

# For API controllers that also serve web clients:
class ApiController < ActionController::API
  include ActionController::RequestForgeryProtection
  protect_from_forgery with: :exception
end
2

Include CSRF meta tags

Add csrf_meta_tags to your layout for JavaScript frameworks.

<!-- app/views/layouts/application.html.erb -->
<head>
  <%= csrf_meta_tags %>
  <!-- This generates: -->
  <!-- <meta name="csrf-param" content="authenticity_token"> -->
  <!-- <meta name="csrf-token" content="..."> -->
</head>

<!-- JavaScript usage -->
<script>
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

fetch('/api/update', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken,
  },
  body: JSON.stringify(data),
});
</script>
3

Remove skip_before_action for CSRF

Replace CSRF bypasses with proper token handling.

# UNSAFE
class PaymentsController < ApplicationController
  skip_before_action :verify_authenticity_token
  def create
    # No CSRF protection!
  end
end

# SAFE - only skip for webhook with its own auth
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token, only: [:stripe]
  def stripe
    sig = request.env['HTTP_STRIPE_SIGNATURE']
    # Verify Stripe webhook signature
  end
end

Prevention Best Practices

1. Include protect_from_forgery in ApplicationController for web-serving apps. 2. Include csrf_meta_tags in your layout for JavaScript access. 3. For API mode with cookie auth, add CSRF middleware back. 4. Use form_with which includes authenticity tokens automatically. 5. Configure Turbo/Hotwire to send CSRF tokens.

How to Test

1. Search for skip_before_action :verify_authenticity_token in controllers. 2. Check if protect_from_forgery is in ApplicationController. 3. Verify csrf_meta_tags is in the layout. 4. Submit a cross-origin form to your POST endpoints. 5. Use Vibe App Scanner to detect CSRF weaknesses in your Rails application.

Frequently Asked Questions

Does Rails API mode include CSRF protection?

No. Rails API mode (ActionController::API) does not include CSRF middleware. If your API serves web clients with cookie-based auth, you must add CSRF protection by including ActionController::RequestForgeryProtection in your controllers.

Does Turbo/Hotwire handle CSRF automatically?

Yes. Turbo automatically includes the CSRF token from the meta tag in form submissions and Turbo Stream requests. Ensure csrf_meta_tags is in your layout. For custom fetch/AJAX requests, you still need to manually include the token.

What does protect_from_forgery with: :null_session do?

It sets the session to null (empty) when CSRF validation fails instead of raising an exception. This silently degrades to an unauthenticated request. Use with: :exception instead to explicitly reject CSRF attacks.

Is Your Rails App Vulnerable to CSRF?

VAS automatically scans for csrf vulnerabilities in Rails applications and provides step-by-step remediation guidance with code examples.

Scans from $5, results in minutes. Get actionable fixes tailored to your Rails stack.