Safeguard
Security Guides•

Rails SQL injection: where() placeholders and safe patterns

find and find_by escape input, but where with a string, execute and find_by_sql need placeholders. Safe and unsafe Active Record patterns side by side.

Safeguard Security Team
2 min read

Active Record escapes input in find and find_by, but raw SQL fragments such as a string passed to where, connection.execute and find_by_sql need manual protection. Use placeholders or the hash form of where, and never use string interpolation of user input inside the SQL text.

What the Rails guide says

The Rails security guide states that Model.find(id) and find_by escape input automatically. SQL fragments passed to methods like where("..."), connection.execute() and find_by_sql() need protection from you.

Unsafe

# User input pasted into SQL text
Project.where("name = '#{params[:name]}'")

A value with a quote can end the string and append its own SQL.

Safe, in three forms

# Positional placeholders
Project.where("zip_code = ? AND quantity >= ?", zip, qty)

# Named placeholders
Project.where("zip_code = :zip AND quantity >= :qty", { zip: zip, qty: qty })

# Hash conditions
Project.where(zip_code: zip)

The guide recommends placeholders, and chaining hash style where calls where possible. Outside models, it points to sanitize_sql.

What placeholders do not cover

They bind values, not identifiers. If the user chooses a sort column or direction, map the input to a fixed list.

SORTABLE = %w[name created_at].freeze
column = SORTABLE.include?(params[:sort]) ? params[:sort] : "created_at"
Project.order(column)

A quick audit

Search for these in your app code:

  1. where(" followed by #{
  2. find_by_sql
  3. connection.execute
  4. order( and group( fed from params

Each one that touches a request value should use a placeholder or an allowlist. Also keep config.filter_parameters set so sensitive parameters stay out of logs, as the guide advises.

Vulnerable gems are a different problem from vulnerable queries. Safeguard's dependency scanning reads your Gemfile.lock for known issues, and you can try it free at gold.safeguard.sh/scan.

Quick check

Is where with a hash always safe?

For values, yes: the hash form binds them. It does not make identifiers like column names safe if you build the hash keys from user input.

Does find_by_sql escape arguments for me?

Not for interpolated strings. The guide lists it among the methods that need manual protection, so pass placeholders.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.