Ruby on Rails Security Guide 2026: CSRF, Strong Params, Brakeman & CSP
Rails security has always been a core framework value. The Rails team ships protections for SQL injection, XSS, CSRF, and session hijacking out of the box. But 'out of the box' is only safe if you know what you have — and what you can accidentally turn off.
Start with a live scan of your deployed application at ZeriFlow — 80+ checks including headers, TLS, and exposed files, free and instant.
1. CSRF Protection in Rails
Rails includes CSRF protection in every non-API controller via protect_from_forgery. It is enabled by default in ApplicationController.
class ApplicationController < ActionController::Base
# Default for full-stack Rails — verified by origin AND token
protect_from_forgery with: :exception
# For API-only applications using cookie sessions
# protect_from_forgery with: :null_session
endIn your layout, the csrf_meta_tags helper embeds the token for JavaScript clients:
<head>
<%= csrf_meta_tags %>
</head>For Rails API mode with JWT in Authorization headers, CSRF is not applicable because the token is not sent automatically by the browser. Do not re-enable cookie auth in API-only apps without also re-enabling CSRF protection.
Never do this in a full-stack app:
skip_before_action :verify_authenticity_token # Disables CSRF — only for APIs2. Strong Parameters
Strong parameters prevent mass assignment vulnerabilities by requiring explicit whitelisting of permitted attributes.
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to @user
else
render :new, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
# Never use params.require(:user).permit! — this allows ALL attributes
end
endFor nested attributes:
def article_params
params.require(:article).permit(
:title, :body, :published,
tags_attributes: [:id, :name, :_destroy]
)
endStrong parameters also protect against injecting admin: true or role: 'superuser' through a form.
3. Brakeman: Static Security Analysis
Brakeman is a static analysis tool that scans your Rails application for security vulnerabilities. It should run in your CI pipeline on every pull request.
gem install brakeman
brakeman -q -w2 --no-pagerCommon findings Brakeman catches:
- SQL injection in where() with string interpolation
- render calls with user-controlled template names
- Redirect to user-supplied URLs
- eval or send with user input
- Missing CSRF protection
Add to CI (GitHub Actions):
- name: Run Brakeman
run: |
gem install brakeman
brakeman -q --exit-on-warn--exit-on-warn fails the build on any warning — treat security warnings as build failures.
4. Content Security Policy DSL
Rails 6+ includes a built-in Ruby DSL for setting Content Security Policy headers.
# config/initializers/content_security_policy.rb
Rails.application.configure do
config.content_security_policy do |policy|
policy.default_src :self
policy.font_src :self, 'https://fonts.gstatic.com'
policy.img_src :self, :data, 'https:'
policy.object_src :none
policy.script_src :self
policy.style_src :self, 'https://fonts.googleapis.com'
policy.connect_src :self
# Specify URI for violation reports
policy.report_uri '/csp-violation-report'
end
# Enable nonce for inline scripts (use with Rails UJS or Stimulus)
config.content_security_policy_nonce_generator = ->(_request) {
SecureRandom.base64(16)
}
config.content_security_policy_nonce_directives = %w[script-src]
# Report-only mode for testing
# config.content_security_policy_report_only = true
endUse content_security_policy at the controller level to override per-action:
class InlineEditorController < ApplicationController
content_security_policy do |p|
p.style_src :self, :unsafe_inline
end
end5. Force SSL and Secure Cookies
In config/environments/production.rb:
Rails.application.configure do
# Force HTTPS for all requests
config.force_ssl = true
# Session cookie configuration
config.session_store :cookie_store,
key: '__Host-session',
secure: true,
httponly: true,
same_site: :lax,
expire_after: 30.minutes
endThe __Host- prefix on the cookie name enforces additional browser security:
- The Secure flag is required
- The Domain attribute must be absent
- The Path attribute must be /
This prevents subdomain cookie injection attacks.
6. Bundler Audit: Dependency Vulnerability Scanning
gem install bundler-audit
bundle-audit update # Update vulnerability database
bundle-audit checkAdd to CI:
- name: Bundle Audit
run: |
gem install bundler-audit
bundle-audit update
bundle-audit check --ignore CVE-XXXX-XXXX # Only ignore with justificationAlso consider ruby_audit for Ruby interpreter vulnerabilities:
gem install ruby_audit
ruby-audit checkCombine code-level auditing with a live scan — ZeriFlow tests your deployed application for header misconfiguration, TLS issues, and exposed paths that static analysis cannot see.
FAQ
### Q: Does Rails protect against SQL injection automatically?
A: Yes — ActiveRecord uses parameterized queries. The risk arises when using string interpolation in where() or find_by_sql(). Always pass conditions as hashes or arrays: User.where(email: params[:email]) or User.where('email = ?', params[:email]).
### Q: What is the safest way to handle file uploads in Rails?
A: Use Active Storage with cloud storage (S3 or GCS). Validate content type using content_type_must_be_a_pdf or a custom validator, set a maximum file size, and never trust the content type sent by the browser. Use Marcel for server-side MIME detection.
### Q: Is Rails' default XSS protection sufficient?
A: Rails auto-escapes all ERB output (<%= ... %>) which prevents most reflected XSS. The risk comes from html_safe, raw(), and sanitize() with permissive options. Use sanitize() only with a strict allowlist and avoid html_safe on user-controlled content.
### Q: How do I secure Rails API tokens?
A: Use has_secure_token for simple API tokens. Store a digest of the token in the database using has_secure_token :api_key, format: :base58. For OAuth-based flows, use Doorkeeper.
### Q: Should I upgrade to the latest Rails minor version immediately?
A: Yes for security patches — Rails follows semantic versioning and security fixes are backported to supported minor versions. Subscribe to the rubyonrails-security mailing list and upgrade within 72 hours of a security release.
Conclusion
Rails security in 2026 is a mature discipline. The framework provides CSRF protection, strong parameters, auto-escaping XSS protection, and secure defaults for sessions. Your job is to avoid disabling them by accident, run Brakeman in CI, keep Bundler audit green, and enforce HTTPS and CSP in production.
The final check is always from the outside. Run a free ZeriFlow scan after every deploy to verify your headers, TLS configuration, and public exposure — the same view your attackers have.