A Practical Security Checklist for Ruby on Rails Applications
The handful of repeat offenders behind most Rails security incidents, in the order we check for them during an audit.
Published February 4, 2024 · 5 min read
Most Rails security incidents we've been called in to review after the fact trace back to a handful of repeat offenders, not zero-days. Here's the checklist we actually run during an audit, in the order we run it.
1. Mass assignment and strong parameters
Still the single most common finding: a controller that permits more attributes than the form actually sends, often because someone added a blanket permit to make a stubborn test pass and never removed it. Every permit list should be a deliberate, reviewed allowlist, never a shortcut.
2. Authorization, not just authentication
A session cookie tells you who's logged in. It says nothing about what that user is allowed to touch. We regularly find controller actions that check whether a user is present and stop there, letting any authenticated user load another account's record by guessing an id. Pundit or CanCanCan policies, applied consistently and tested, close this gap; ad hoc checks scattered across controllers don't.
class InvoicePolicy < ApplicationPolicy
def show?
record.account_id == user.account_id
end
end
3. Raw SQL and string interpolation
ActiveRecord protects you by default, until someone drops to raw SQL for a quick report and interpolates a parameter directly into the string. Always bind parameters, even in a rake task nobody expects to run twice.
# Vulnerable
Invoice.where("status = '" + params[:status] + "'")
# Safe
Invoice.where(status: params[:status])
4. Secrets in the repository
Rails credentials solve this cleanly, but only if every environment actually uses them. We still find API keys hardcoded in initializers "temporarily," committed years earlier.
5. Dependency freshness
An audit tool running in CI, actually blocking merges, not just logging a warning nobody reads. A known vulnerability sitting in a lockfile for eight months is the easiest finding in any review.
6. Rate limiting on anything that touches money or auth
Login, password reset and payment endpoints without rate limiting are an open invitation to credential stuffing. A request-throttling middleware covers most of this in a few lines of configuration.
None of this is advanced. That's exactly why it's worth checking on a schedule instead of hoping someone remembers.
Related articles
Handling Sensitive Health Data in Rails: Security and Compliance Basics for HealthTech
Building a Rails application that touches health data changes the baseline. Notes on field-level encryption, access auditing, and HDS hosting requirements.
Designing a Rails Service Architecture Around SEPA and Payment Flows
Payments change the design questions a Rails codebase has to answer. Notes on isolating money movement, ledgers, and modeling SEPA as the asynchronous process it is.