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:
where("followed by#{find_by_sqlconnection.executeorder(andgroup(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.