No analysis of Rails security would be complete without examining the environment that Ruby lives in.
The Kernel.system
method 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 of system
, 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
The message passed in to that method will always be the third
parameter to svn
, no matter what
kind of shell metacharacters it contains.
Tainting is an idea that came to Ruby from
Perl. Because data that comes from the outside is not to be trusted,
why not force it not to be trusted by default? Any data read from the
environment or outside world is marked as tainted. Depending on the
current value of a special Ruby global, $SAFE
, certain operations are prohibited on
tainted data. Objects may only (officially) be untainted by calling
their untaint
method.
This is a good idea that, because of implementation details, has not gained much traction in the Rails community. It can become a pain to deal with every piece of data that was derived from user input. There is one Rails plugin, safe_erb, which leverages tainting to ensure that all user-supplied data is HTML-escaped before being displayed again. Request parameters and cookies are tainted upon each request, and an error is raised if tainted data is attempted to be rendered. (The Ruby tainting facility is not used other than as a flag on the objects, because anything more would require a $SAFE level greater than zero, which is Rails-unfriendly.) This reduces the possibility of cross-site scripting attacks. The plugin is available at http://agilewebdevelopment.com/plugins/safe_erb.