Join our Newsletter — 33% off our NHI Course
Home› Guides› Ruby on Rails Security Guide: Framework Defaults, Secrets…
Guide Application Security

Ruby on Rails Security Guide: Framework Defaults, Secrets and Safe Patterns

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 26 October 2026 16 min read
On this page

Rails ships with strong security defaults, but most serious Rails vulnerabilities come from code and configuration that quietly step around them. String-built SQL fragments, html_safe on user content, a leaked master.key, a permit! call or a missing config.hosts entry each undo a protection the framework already provides. This guide covers what Rails protects by default, the configuration settings that matter in production, safe and unsafe coding patterns, credentials and key management, and the dependency and scanning workflow. It is written for Rails developers, application security engineers and reviewers who need specific settings and method names rather than general advice.

Key takeaways

  • Know which defaults you inherit. Encrypted cookie sessions, CSRF tokens, output escaping, parameterised Active Record finders and four default security headers are on in a new app; config.load_defaults decides several of them, so an old value there means old behaviour.
  • Most Rails bugs are bypasses. Interpolated strings in where, raw and html_safe, redirect_to params[:url], render file: with user input and permit! are the recurring review findings.
  • The master key is the crown jewel. config/master.key or RAILS_MASTER_KEY decrypts every secret in credentials.yml.enc, and secret_key_base signs and encrypts every session cookie. Treat both as production secrets with an owner and a rotation plan.
  • Production configuration is a security control. force_ssl, config.hosts, filter_parameters, raise_on_open_redirects and consider_all_requests_local = false belong in a reviewed baseline, not in individual developer memory.
  • Use the scanners Rails now generates. New apps include Brakeman and bundler-audit in the Gemfile and the generated CI workflow; keep them, gate merges on them and stay on a supported Rails series.

What Rails protects by default

Rails security is a set of layered defaults across Action Pack, Action View and Active Record. The table shows what each default does, where it is controlled and how it is typically broken. Defaults tied to config.load_defaults only apply when the app's target version is new enough, which is why upgraded applications often lag behind freshly generated ones.

ProtectionDefault behaviourSetting or APIHow it is usually bypassed
Session integrityActionDispatch::Session::CookieStore, cookie encrypted and signed with keys derived from secret_key_baseconfig.session_store, credentialsLeaked secret_key_base, secrets committed to Git
CSRFForgery protection on ActionController::Base (default since 5.2 load defaults)protect_from_forgery, csrf_meta_tagsskip_forgery_protection on state-changing actions, GET routes that change state
Output escapingERB <%= %> escapes HTMLAction View helpers, sanitizeraw, html_safe, <%== %>, unsafe link_to hrefs
SQL parameterisationHash conditions and finders such as find and find_by(email: x) quote valuesActive Record query interfaceString interpolation in where, order, pluck, find_by_sql
Mass assignmentStrong parameters reject unpermitted keys; permit_all_parameters is falseparams.expect, params.require(...).permit(...)permit!, permitting role or admin keys
Security headersX-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, X-Permitted-Cross-Domain-Policies: none, Referrer-Policy: strict-origin-when-cross-originconfig.action_dispatch.default_headersdefault_headers.clear, removing headers to allow framing
Host headerActionDispatch::HostAuthorization active in development onlyconfig.hosts, config.host_authorizationNever configured in production, so the protection is absent
Log filteringGenerated initialiser filters :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvcconfig.filter_parametersCustom parameter names (for example api_credential) that no pattern matches

See the OWASP Top 10 Guide for the vulnerability classes these defaults address, and the Security Headers Guide for header values beyond the Rails defaults.

Production configuration baseline

Most of the Rails attack surface that is not in application code lives in config/environments/production.rb and config/application.rb. Review these settings in every release and treat a change to them as a security change.

SettingRecommended production valueWhy it matters
config.force_ssltrueRedirects to HTTPS, marks cookies secure and adds Strict-Transport-Security via ActionDispatch::SSL; config.ssl_options includes subdomains in HSTS from the 5.0 defaults onwards
config.assume_ssltrue only behind a TLS-terminating proxyStops redirect loops and HTTP cookie targeting when the proxy forwards plain HTTP
config.hostsExplicit list of production host namesEnables HostAuthorization against DNS rebinding and Host header poisoning of generated URLs and password reset links; exclude health checks with config.host_authorization = { exclude: ... }
config.consider_all_requests_localfalsetrue returns full exceptions and exposes /rails/info/properties
config.require_master_keytrueApp refuses to boot without a key, rather than starting with missing credentials
config.action_controller.raise_on_open_redirectstrueRaises UnsafeRedirectError for external hosts unless the call passes allow_other_host: true
config.action_controller.forgery_protection_origin_checktrueAdds an Origin header check to CSRF token verification
config.action_controller.default_protect_from_forgery_with:exceptionThe historical default is :null_session; the Rails configuration guide records :exception as the new default from the 8.2 load defaults, and older apps can opt in early
config.action_controller.action_on_unpermitted_parameters:raise in test, :log in developmentTurns silent parameter drops into failing tests, which catches mass assignment mistakes early
config.action_dispatch.cookies_same_site_protection:lax or :strictAdds a browser-side CSRF defence for cookies

Run bin/rails app:update on each upgrade and work through new_framework_defaults_*.rb so that the app's load_defaults value moves forward with the framework. A Rails 8 app still running with load_defaults 5.1 silently lacks several protections in the table above.

Safe and unsafe coding patterns

The Rails security guide and the OWASP Ruby on Rails Cheat Sheet agree on a short list of methods that deserve a mandatory second look in code review. Each row below pairs the risky pattern with its safe replacement.

RiskUnsafe patternSafe pattern
SQL injectionProject.where("name = '#{params[:name]}'"), interpolated order or find_by_sqlwhere(name: params[:name]), positional where("zip = ?", zip) or named placeholders, sanitize_sql, an allow-list for sort columns
XSSraw(user_input), user_input.html_safe, link_to "x", params[:url]Default ERB escaping, sanitize(input, tags: [...], attributes: %w(href title)) with a permitted list, URL scheme validation
Mass assignmentUser.new(params[:user]) with permit!params.expect(user: [:name, :email]), never permit privilege fields from user forms
Command injectionsystem("convert #{file}"), backticks, exec, spawn with a single stringsystem("convert", file) (argument form), allow-listed values
Kernel#openopen(params[:path]) (a leading pipe runs a command)File.open, URI.open on a validated URI
Path traversalsend_file("/var/www/uploads/" + params[:filename]), render file: params[:template]Look up files by record ID, check the expanded path stays under the intended directory, never render user-chosen templates
Open redirectredirect_to params[:return_to]raise_on_open_redirects, url_from(params[:return_to]) or a path-only allow-list
Regex validation/^https?:\/\/[^\n]+$/\A and \z anchors; Rails format validators raise if ^ or $ is used without multiline: true
Code executioneval, constantize or send on user inputExplicit mapping hashes from input to allowed classes or methods

Path traversal is not theoretical in Rails: CISA's Known Exploited Vulnerabilities catalogue lists three Rails file disclosure flaws, including CVE-2019-5418, where crafted Accept headers combined with render file: disclosed arbitrary files. See the SQL Injection Prevention Guide, the Cross-Site Scripting (XSS) Prevention Guide and the Input Validation and Output Encoding Guide for the general techniques.

Authorisation is your code, not the framework's

Rails has no built-in object-level authorisation. Project.find(params[:id]) returns any project to any signed-in user. Scope lookups through the owner (current_user.projects.find(params[:id])) or a policy layer, and test that one tenant cannot read another tenant's records. This is the insecure direct object reference that the OWASP cheat sheet lists, and the one Brakeman cannot fully detect because it depends on business rules.

Sessions, authentication and CSRF

  • Cookie sessions: the default CookieStore keeps the session in the client, encrypted. The Rails guide notes a 4 kB size limit and warns that clients may keep and replay old cookies, so store identifiers rather than sensitive data and do not keep values such as account balances in the session.
  • Session fixation: call reset_session after a successful sign-in so a pre-login session identifier cannot be reused. The authentication generator in recent Rails and gems such as Devise handle this on sign-in and sign-out; hand-rolled login code often does not. See the Session Fixation Guide.
  • Server-side expiry: because a client can edit cookie expiry, expire sessions on the server. With the database-backed sessions created by the authentication generator, delete records older than your idle and absolute limits on a schedule.
  • Passwords: has_secure_password stores bcrypt hashes and authenticate_by looks up and verifies credentials in one call, which avoids timing differences between unknown and wrong-password accounts.
  • Brute force: Rails 8 includes a controller rate limiter. rate_limit to: 10, within: 3.minutes, only: :create on the sessions controller is the example in the Rails security guide; pair it with generic error messages.
  • CSRF: keep protect_from_forgery with: :exception and csrf_meta_tags in the layout, use non-GET verbs for every state change, and never add skip_forgery_protection to a cookie-authenticated controller. API controllers authenticated by bearer tokens inherit from ActionController::API, which does not include forgery protection, and must not also accept the session cookie. See the CSRF Prevention Guide.

Credentials, secret_key_base and encryption

Rails stores application secrets in config/credentials.yml.enc, edited with bin/rails credentials:edit and decrypted with config/master.key or the RAILS_MASTER_KEY environment variable. The encrypted file may be committed; the key must never be. Per-environment files such as config/credentials/production.yml.enc with their own keys let you restrict who can read production secrets.

SecretWhat it protectsExposure impactRotation approach
master.key / RAILS_MASTER_KEYEvery value in the credentials fileAll API keys, database passwords and secret_key_base readableRe-encrypt credentials under a new key, then rotate every secret the file held
secret_key_baseSigned and encrypted cookies, sessions, message verifiers, signed IDsForged sessions and cookies for any userAdd a cookie rotator reading the old value (for example old_secret_key_base), deploy the new value, remove the old one once cookies have expired
Active Record encryption keysAttributes declared with encryptsDecryption of protected columnsGenerate with bin/rails db:encryption:init; keep previous keys configured until data is re-encrypted
Third-party API keysPayment, email, cloud and AI servicesDirect abuse of the provider accountRotate in the provider, update credentials, monitor provider audit logs
  • Fail loudly: use Rails.application.credentials.some_api_key! so a missing secret raises a KeyError instead of sending blank values to a provider.
  • Protect columns, not just transport: Active Record encryption is non-deterministic by default; use deterministic: true only for columns you must query by exact match, such as an email address, because deterministic ciphertexts reveal equality.
  • Keep secrets out of build output: container images built with RAILS_MASTER_KEY as a build argument keep it in image history. Supply the key at runtime from a secrets manager. See the Secrets Management Guide.

Where identity fits

A production Rails app is itself a non-human identity: it holds a database role, cloud credentials for Active Storage, SMTP and payment API keys, and often a RubyGems or GitHub token in its CI pipeline. Each of these should be scoped to the app, owned by a named team, rotated on a schedule and revocable without a redeploy. Background jobs (Solid Queue, Sidekiq) usually run with the same credentials as the web process; consider separate, narrower credentials for job workers that call external services. See the OWASP NHI Top 10 NHI7 Long-Lived Secrets Guide.

Headers, CSP and browser controls

Rails provides a Content Security Policy DSL in config/initializers/content_security_policy.rb, with per-request nonces through content_security_policy_nonce_generator and a report-only mode (content_security_policy_report_only = true) for testing. Start in report-only mode with a report_uri, fix violations, then enforce. A Permissions Policy DSL lives in config/initializers/permissions_policy.rb. Rails has no built-in CORS middleware; the Rails guide uses the rack-cors gem in config/initializers/cors.rb, and origins there should be an explicit list, never * with credentials. See the Content Security Policy Guide and the Clickjacking Guide.

Dependencies, scanning and upgrades

  • Static analysis: new Rails apps include brakeman in the Gemfile and a generated CI job that runs it. Brakeman understands Rails data flow and finds SQL injection, XSS, unsafe redirects, mass assignment and dangerous render calls. Review its ignore file (config/brakeman.ignore) like code.
  • Gem advisories: new apps also include bundler-audit, which checks Gemfile.lock against the Ruby Advisory Database; ignored advisories go in config/bundler-audit.yml and should carry an expiry and an owner.
  • Conservative updates: the Rails guide recommends bundle update --conservative gem_name so that patching one vulnerable gem does not pull unrelated upgrades.
  • Supported series: the Rails maintenance policy gives each minor series bug fixes for one year and security fixes for two years from its first release. When this guide was written, 8.1 received security fixes until 10 October 2027 and 8.0 until 7 November 2026; older series get no new releases.
  • Registry trust: the GemStuffer RubyGems campaign pushed thousands of malicious gems and tried to harvest maintainers' API keys. Pin versions in Gemfile.lock, review new gems before adding them, and enable MFA on RubyGems accounts. See the Malicious Package and Dependency Confusion Guide.

How to implement: a phased rollout

  1. Baseline (week 1): record each app's Rails version, load_defaults value and production configuration against the baseline table. Flag any app on an unsupported series.
  2. Scanners in report mode (weeks 1 to 2): add Brakeman and bundler-audit to CI without failing builds, triage the backlog and record accepted findings with an owner.
  3. Secrets (weeks 2 to 4): confirm master.key is in .gitignore, search Git history for leaked keys, move RAILS_MASTER_KEY into a secrets manager and rotate anything that was ever committed.
  4. Configuration hardening (weeks 3 to 6): set config.hosts, force_ssl, raise_on_open_redirects and origin checks in staging first; run the full test suite and smoke tests; roll back by reverting the config commit if health checks or redirects fail.
  5. CSP (weeks 4 to 8): deploy a report-only policy, collect violations for at least one release cycle, then enforce. Rolling back is a one-line switch back to report-only.
  6. Gate and maintain (ongoing): fail merges on new high-confidence Brakeman warnings and unpatched advisories, and schedule framework upgrades before the series leaves security support.

Small teams can stop after steps 1 to 4 plus the scanners and still remove most of the common risk. Larger organisations with many Rails services should publish the baseline as a shared template or generator and measure drift against it.

Detection and measurement

  • Log signals: ActionController::InvalidAuthenticityToken spikes (CSRF probing or a broken form), ActionDispatch::HostAuthorization blocked hosts, UnsafeRedirectError, rate limiter rejections on sessions and password reset, and ActiveRecord::StatementInvalid errors with quote characters in parameters.
  • Log hygiene: test that filter_parameters masks every secret-bearing field by grepping production logs for known parameter names.
  • Metrics: percentage of apps on a supported Rails series; open Brakeman high-confidence warnings per app; median days from a Ruby advisory to patched production; number of apps with load_defaults more than one major version behind; apps with an enforced CSP; days since last secret_key_base or master key rotation.

Framework mapping

Rails control areaNIST SP 800-53 Rev. 5NIST CSF 2.0OWASP ASVS 5.0MITRE ATT&CK addressed
Parameterised queries, escaping, strong parametersSI-10PR.PS-06V1.2.4, V15.3.3T1190 Exploit Public-Facing Application
Sessions and cookie keysSC-23PR.AA-05V3.3.1T1539 Steal Web Session Cookie
TLS, HSTS, headersSC-8PR.DS-02V3.4.1, V3.4.4
Credentials and encryption keysSC-12, SC-28PR.DS-01V13.3.1T1552.001 Credentials In Files
Brakeman, bundler-audit, upgradesSA-11, RA-5, SR-3ID.RA-01V15.2.1T1195.002 Compromise Software Supply Chain
Production error pagesSA-15PR.PS-06V13.4.2

In CIS Controls v8.1 this work sits in Control 16 (Application Software Security), and in ISO/IEC 27001:2022 Annex A in 8.25 (secure development life cycle), 8.28 (secure coding) and 8.24 (use of cryptography).

Common failure patterns

  • Committed master key: config/master.key is added to a repository or baked into a container image, exposing every credential at once. Exposed .git folders and public images are both common routes for such leaks.
  • Stale load_defaults: an app upgraded across several majors without updating load_defaults runs with :null_session CSRF handling, no origin check and older cookie settings.
  • Interpolated order and pluck: developers who parameterise where still pass params[:sort] into order, which takes raw SQL.
  • html_safe on helper output: a helper that concatenates user data and calls html_safe turns every view that uses it into an XSS sink.
  • Unscoped finders: Model.find(params[:id]) in multi-tenant apps exposes other customers' records; scanners rarely catch it.
  • render file: with user input: the CVE-2019-5418 pattern; even on patched Rails, choosing a template from a parameter remains a path traversal risk.

Practitioner checklist

  • Confirm every app runs a Rails series inside its security support window and that load_defaults matches the framework version.
  • Set force_ssl, config.hosts, require_master_key, raise_on_open_redirects and consider_all_requests_local = false in production.
  • Keep master.key out of Git and images; supply RAILS_MASTER_KEY at runtime and document how to rotate it and secret_key_base.
  • Ban permit!, string-interpolated SQL, raw/html_safe on user data and render file: with parameters through code review rules and Brakeman.
  • Call reset_session on sign-in, expire sessions server-side and rate limit login and password reset.
  • Scope every record lookup to the current user or tenant and test cross-tenant access.
  • Run Brakeman and bundler-audit in CI with reviewed ignore files that carry owners and expiry dates.
  • Extend filter_parameters for custom secret field names and verify logs are clean.
  • Deploy a nonce-based CSP in report-only mode, then enforce it.

Standards and references

Related NHI Mgmt Group resources: Django Security Guide · Secure Code Review Guide · Software Supply Chain Security Guide · Secrets Management Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 26 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org