For: anyone running a Ruby on Rails app. No attack steps here — this is a working reference for hardening: a priority-ordered checklist, per-area guidance, and self-verification. For the cross-framework picture, see the security-by-framework hub.
Priority-ordered hardening checklist
Do this table top-down. P0 is a prerequisite, P1 is the most frequent source of incidents, P2 is ongoing maintenance.
P0 ── Prerequisite (do first)
Secrets & credentials management / production config (no exception exposure, force_ssl) / gem CVEs patched fast
P1 ── Top source of incidents
Strong Parameters/Mass Assignment control / authorization (owner scope)
P2 ── Ongoing maintenance
Injection and dangerous methods / sessions, CSRF / SSRF, uploads
| Priority | Control | Specifics (Rails) |
|---|---|---|
| P0 | Secrets & credentials | Encrypted credentials + separate master key. Don't commit the master key. Rotate secret_key_base on leak |
| P0 | Production config | Don't expose exception detail; config.force_ssl; filter_parameters to keep secrets out of logs |
| P0 | gem (dependency) CVEs | Monitor with bundler-audit / osv-scanner; judge by running version, patch fast |
| P1 | Strong Parameters | Keep permit minimal. Don't use permit!. Never assign a privilege field |
| P1 | Explicit authorization | Pundit / CanCanCan + authorize + current_user scoping for ownership |
| P2 | Injection/dangerous methods | Bind in where. Don't pass input to send/constantize. Don't load untrusted YAML/Marshal |
| P2 | Sessions/CSRF | protect_from_forgery (default), cookie secure/httponly/samesite, regenerate on login |
| P2 | SSRF/uploads | Allowlist server-side fetches + block internal IPs. Validate uploads, store outside public/ |
1. Secrets and credentials (P0)
- Use Rails's encrypted credentials, and don't commit the master (decryption) key to the repo (inject it via env, etc.).
secret_key_baseunderpins signed/encrypted cookies and sessions. Rotate promptly if leaked (note this invalidates existing signatures/encryption).- Don't leave
.env, dumps, or backups in a public directory (→ keep secrets out of public directories).
2. Production config (P0)
- In production, don't expose exception detail (don't enable
consider_all_requests_local, etc.) — reduce internal-structure disclosure. config.force_sslto force HTTPS and mark cookies secure. Don't enable dev tools like the web console in production.- Use
filter_parametersto keep passwords and the like out of logs.
3. gem (dependency) CVEs (P0)
- Machine-monitor gem CVEs with bundler-audit or osv-scanner, judge by the running version (Gemfile.lock), and patch fast (→ monitoring dependency CVEs · the vulnerability-response playbook).
- Keep Rails and Ruby on supported versions and don't leave EOL versions in place.
4. Strong Parameters and Mass Assignment (P1)
- Keep
permitfields to the minimum, and avoidpermit!(allow-all). - Never assign a privilege field like
is_adminfrom user input. Drop "permit broadly because it's convenient."
5. Authorization (P1 — the top source)
Common (dangerous)
- no authorization — "logged in = can view/update"
- route-model binding fetches another user's ID
- relying on hidden routes / hard-to-guess IDs
- forgetting
authorizeon some actions
Correct
- policy explicit with Pundit / CanCanCan +
authorizeper action - reads too are owner-scoped by
current_user - verified on every read/update/delete path
- authorization built explicitly, not left to defaults
See what IDOR is. Authentication and authorization differ; authorize close to the data.
6. Injection and dangerous dynamic methods (P2)
- SQL: bind with placeholders in
where; never build queries by string concatenation (avoidwhere("... #{params}")) (→ what SQL injection is). - Dangerous dynamic methods: don't pass user input to
send/public_send/constantize. If you must, restrict strictly with an allowlist. - Deserialization: avoid loading untrusted
YAML/Marshal(can lead to code execution under the right conditions).
7. Sessions, cookies, CSRF (P2)
- Don't globally disable CSRF protection (
protect_from_forgery); use the form token (→ what CSRF is). - Set secure/httponly/samesite on cookies/sessions, and regenerate the session on login.
8. SSRF, uploads, headers (P2)
- Server-side fetching of user-supplied URLs should restrict targets to an allowlist and block reaching internal IPs/metadata (→ what SSRF is).
- Validate uploads (type/size), store outside
public/, and grant no execute permission. - Add security headers (check your own site with the security headers checker).
Verify: is your Rails actually hardened?
Building it isn't the end — it's done only once you've checked. These are defensive self-checks against your own environment.
Secrets aren't exposed/committed
master.key isn't in the repo, and that .env/dumps aren't fetchable by URL.No exception detail in production
Authorization holds
Dependencies and headers
bundler-audit/osv is clean, and that HTTPS/HSTS are present via the headers checker.This site's view: even with safe defaults, authorization and dependencies are your responsibility
Rails's good defaults remove a lot of risk, but authorization — who may do what — and keeping dependencies up to date are app- and operation-specific, so a framework can't guard them automatically. The incidents we keep seeing are less elaborate attacks than the "authenticated but no ownership check" type. So the focus is working through the table above from the top: tighten Strong Parameters, make authorization explicit, and monitor gems for CVEs.
Read next
- Hub: security by framework · Laravel security (same MVC, similar authorization issues)
- Practice: the vulnerability-response playbook · monitoring dependency CVEs · keep secrets out of public directories
- Glossary: what IDOR is · SQL injection · CSRF · SSRF
- Tools: security headers checker
Sources and scope
These primary references support the settings, scope or remediation discussed above; links were checked on October 23, 2026. Consult their version and configuration conditions. Checklist priorities and lessons are this site's guidance, not a full-article revalidation or a safety guarantee.