Ruby’s Environment | 145
Another typical example of SQL injection is a query such as “'OR1=1;--”, which
yields:
SELECT * FROM people WHERE name = '' OR 1 = 1; --%';
This query would return all records from thepeopletable. This can have security
implications, especially when this sort of code is found in authentication systems.
For applications written against the standard APIs, Rails is amazingly well protected
against SQL injection attacks. All of the standard finders and dynamic attribute find-
ers sanitize single attribute arguments, but there is only so much that they can do.
Remember the cardinal rule:never interpolate user input into a SQL string.
Most of the Rails finders that accept SQL also accept an array, so you can turn code
like"SELECT FROM people WHERE name = '#{search_name}'"into["SELECT FROM people
WHERE name = ?", search_name]nearly anywhere. (Note the lack of quoting around the
question mark; Rails interprets the type ofsearch_nameand quotes it appropriately.)
The user-provided name value will have any special SQL characters escaped, so you
don’t have to worry about it.
For any situations where you need to do this quoting yourself, you can steal the pri-
vatesanitize_sql method fromActiveRecord::Base (just don’t tell anyone):
class << ActiveRecord::Base
public :sanitize_sql
end
name = %(O'Reilly)
puts ActiveRecord::Base.sanitize_sql([%(WHERE name = ?), name])
# >> WHERE name = 'O''Reilly'
Ruby’s Environment
No analysis of Rails security would be complete without examining the environment
that Ruby lives in.
Using the Shell
The Kernel.systemmethod is useful for basic interaction with system services
through the command line. As with SQL, though, it is important to ensure that you
know exactly what is being passed, especially if it comes from an external source.
The best way to protect against malicious user input making it to the shell is to use
the multiparameter version ofsystem, only passing the command name in the first
parameter. The subsequent parameters are shell-escaped and passed in, which makes
it much harder to slip something into the command line unnoticed:
def svn_commit(message)
system("/usr/local/bin/svn", "ci", "-m", message)
end