A good Ruby secure coding checklist is short enough to use in code review: search for dangerous methods, check that every record lookup is authorised, constrain redirects and file paths, protect sessions, keep secrets out of the repository, and keep gems current. Here are twelve checks, each with the code smell to grep for.
What do you search for before you read a diff?
Start with a few text searches, because they find the riskiest lines fast.
- Dynamic code and shell calls. OWASP's Rails cheat sheet lists the Ruby functions that build code or run commands from strings:
eval,system, backticks,exec,spawn, andopenwith a pipe, plus severalIOmethods that accept a leading pipe. Its advice is that this is usually a bad idea, and that if you must, you use an allow-list and validate input thoroughly. Search for each, and treat any user-influenced argument as a defect. - String-built SQL. A where clause assembled with string addition is injectable. The cheat sheet shows the fix: placeholders, and
sanitize_sql_likewhen the value goes inside a pattern. - Escaping bypasses.
raw,html_safeand similar calls turn off the default output escaping. OWASP sayshtml_safeis confusingly named and itself unsafe: it asserts that the string is safe, it does not make it so. For rich text, accept a markup language such as Markdown and disallow HTML tags.
What do you check in controllers?
- Authorisation per record. The cheat sheet notes that a vanilla Rails app has no built-in protection against a user changing an id in a guessable RESTful path to reach someone else's data. Authorise each action, by hand or with a resource-based library such as Pundit or CanCanCan. Ask of every
find: whose record is this? - Redirects. The Rails guide says passing user input directly into
redirect_tois generally dangerous. Compare against an allow-list of paths or hosts. - File access.
send_filewith a name from params lets someone pass../../style input and download any file. The guide recommends normalising the path and confining it to a directory, or storing files under database ids. - Parameters. Permit only the attributes a form should set, and review any new permitted key like a new public API field.
- Forgery protection. The Rails guide says CSRF protection is on by default for newly created apps, using a security token in non-GET requests. Check nobody skipped it for an action that changes data, and remember XSS bypasses it.
What about sessions, secrets and logs?
- Sessions. Reset the session after sign in, as the guide recommends, to defeat fixation. Expire sessions on the server and not only in the cookie, and turn on
force_ssl. The default cookie store keeps the session in an encrypted cookie, so changingsecret_key_baselogs everyone out and also affects other signed or encrypted data. - Secrets and sensitive files. The OWASP sheet lists files that should be excluded or carefully managed, such as
config/database.yml, the old secret token initializer, seed files with admin users and development databases. Use encrypted credentials. - Logs. Add sensitive parameter names to
filter_parameters. Rails ships defaults that partially match password, secret and token, but a field calledssnorcardis yours to add. - Rate limits. The guide shows a one-line
rate_limiton the sessions controller to slow brute force against logins.
What about gems?
OWASP warns that most gems are not signed and that you should audit what you use and have a defined process for updating dependencies. RubyGems' own security guide adds practical controls: commit the lockfile with checksums, use bundler-audit against the advisory database in CI, give each gem an explicit source, and consider a cooldown on brand new releases. Brakeman, a static scanner for Rails apps, is also named by OWASP as a way to find XSS problems.
A constructed example
A pull request adds a "download invoice" action: send_file with a path built from params[:name], no check that the invoice belongs to the signed in user, and a redirect_to params[:return] after the download. Three checklist items fail in one diff (6, 4 and 5). The fix takes ten lines: look the invoice up through the current user's own invoices, send the stored file by id, and redirect to a named route.
What to do
- Put the twelve checks in your pull request template.
- Run Brakeman and
bundler-auditin CI so the mechanical checks are automatic. - Add the greps to a pre-review script.
- Require a second reviewer for authorisation changes.
- Review gems when they are added and on a schedule.
- Keep Rails and Ruby on supported versions.
Safeguard's code scan and dependency scanning can run these checks in pull requests, as in the pull request gate demo.
Quick check
Does html_safe sanitize a string?
No. It only marks the string as already safe, which skips escaping. Use it only on content you built yourself.
Is CSRF protection enough if the app has XSS?
No. XSS lets an attacker read the token from the page and submit the form, so it bypasses CSRF protection.
Sources
- OWASP: Ruby on Rails Cheat Sheet
- Ruby on Rails Guides: Securing Rails Applications
- RubyGems Guides: Security
- Brakeman documentation
Last reviewed: 18 October 2026.