Ruby’s security system provides a mechanism for writing programs that work with untrusted data and untrusted code. There are two parts to the security system. The first is a mechanism for distinguishing safe data from untrusted, or tainted, data. The second is a technique for restricted execution, which allows you to “lock down” the Ruby environment and prevents the Ruby interpreter from performing potentially dangerous operations on tainted data. This serves to prevent things like SQL injection attacks in which malicious input alters a program’s behavior. Restricted execution can be taken a step further so that untrusted (and possibly malicious) code can be executed without fear that it will delete files, steal data, or otherwise cause harm.
This discussion of Ruby’s security mechanisms is specific to the reference implementation. Other implementations may differ. JRuby, in particular, makes very little attempt (at the time of this writing) to emulate the restricted execution modes of the reference implementation. Furthermore, keep in mind that Ruby’s security model has not received the kind of careful and prolonged scrutiny that Java’s security architecture has. This section explains how Ruby’s security architecture is supposed to work, but bugs yet to be discovered may allow the restrictions to be circumvented.
Every object in Ruby is either tainted or untainted. Literal values in program source code are
untainted. Values that are derived from the external environment are
tainted. These include strings read from the command-line (ARGV
) or environment variables (ENV
) and also any data read from files,
sockets, or other streams. The environment variable PATH
is a special case: it is tainted only
if one or more of the directories it contains is world-writable.
Importantly, taintedness is contagious, so objects derived from
tainted objects are also
tainted.
The Object
methods taint
, tainted?
, and untaint
allow you to mark an untainted
object as tainted, test the taintedness of an object, and untaint a
tainted object. You should untaint a tainted object only if your code
has inspected it and determined that it is safe despite its unsafe
origin or derivation.
In Ruby 1.8, any objects created by untrusted code (i.e. code run under the “Safe level 4” restrictions described below) are also tainted. Furthermore, that untrusted code is not allowed to modify untainted objects.
Ruby 1.9 adds an “untrusted” flag to each object and separates
the notions of taint and trust. In Ruby 1.9, objects created by
untrusted code are both tainted and untrusted, and untrusted code is
not allowed to modify trusted objects, regardless of whether those
objects are tainted or not. You can test whether an object is
untrusted with the untrusted?
method. You can force an
object to be untrusted (so it can be modified by untrusted code, for
example) with the untrust
method,
and you can make an object trusted again with the trust
method. These three Object
methods parallel tainted?
, taint
, and untaint
.
Ruby can execute programs with security
checking turned on. The global variable $SAFE
determines the level of the security
check. The default safe level is normally 0, but is 1 for Ruby
programs that run setuid or
setgid. (These are Unix terms for a program that
runs with privileges beyond those of the user that invokes it.) Legal
safe levels are the integers 0, 1, 2, 3, and 4. You can explicitly set
the safe level with the -T
command-line option to the Ruby interpreter. You can also set the safe
level by assigning to $SAFE
. Note,
however, that you can only increase the value—it is never possible to
lower this value:
$SAFE=1 # upgrade the safe level $SAFE=4 # upgrade the safe level even higher $SAFE=0 # SecurityError! you can't do it
$SAFE
is thread-local. In
other words, the value of $SAFE
in
a thread may be changed without affecting the value in other threads.
Using this feature, threads can be sandboxed for untrusted
programs:
Thread.start { # Create a "sandbox" thread $SAFE = 4 # Restrict execution in this thread only ... # Untrusted code can be run here }
Proc
objects have their own
copy of the global $SAFE
variable.
When a proc or a lambda is invoked with the call
method (but not when invoked like a
block with yield
) it runs at the
safe level in effect when it was defined, not the level in effect when
it is invoked. Furthermore, if you set $SAFE
in a proc or lambda, that setting
remains local. This means that you can sandbox code without creating a
new thread:
# Execute a block at the specified safe level def safely(level = 4) sandbox = lambda do # Set up a sandbox $SAFE = level # Go to the specified safe level for this lambda only yield # Invoke the block at that level end sandbox.call # Invoke the sandbox without changing $SAFE globally end
In this level, potentially dangerous operations using tainted
data are forbidden. You can’t evaluate a string of code if the
string is tainted; you can’t require
a library if the library name is
tainted; you can’t open a named file if the filename is tainted; and
you can’t connect to a network host if the hostname is tainted.
Programs, especially networked servers, that accept arbitrary input
should probably use this safe level. This helps catch programming
errors that use tainted data in unsafe ways.
If you write a library that performs potentially dangerous
operations—such as communicating with a database server—you should
check the value of $SAFE
. If it
is 1 or higher, your library should not operate on tainted objects.
For example, you should not send a SQL query to a database if the
string containing that query is tainted.
Execution restrictions at safe level 1 include the following:
Environment variables RUBYLIB
and RUBYOPT
are ignored at startup.
The current directory (.
) isn’t included in $LOAD_PATH
.
The command-line options -e
, -i
, -I
, -r
, -s
, -S
, and -X
are prohibited.
Certain instance methods and class methods of Dir
, IO
, File
, and FileTest
are prohibited for tainted
arguments.
test
, eval
, require
, load
, and trap
may not be invoked with tainted
arguments.
Safe level 2 restricts operations on tainted data just as level 1 does, but also imposes additional restrictions on how files and processes can be manipulated, regardless of taint. There is little reason for a program to set its own safe level to 2, but a system administrator might choose to run a program you have written at this safe level to ensure that it cannot create or delete directories, change file permissions, launch executables, load Ruby code from world-writable directories, and so on.
Methods restricted at this safe level include:
Dir.chdir File.truncate Process.egid= Dir.chroot File.umask Process.fork Dir.mkdir IO.fctrl Process.kill Dir.rmdir IO.ioctl Process.setpgid File.chmod Kernel.exit! Process.setpriority File.chown Kernel.fork Process.setsid File.flock Kernel.syscall File.lstat Kernel.trap
In addition, safe level 2 prevents you from loading or requiring Ruby code or running executables stored in world-writable directories.
Safe level 3 includes all of the restrictions of level 2, and
in addition, all objects—including literals in program source code
(but not including predefined objects in the global environment)—are
tainted when they are created. In Ruby 1.9, objects created at this
level are untrusted in addition to being tainted. Neither the
untaint
nor the trust
methods may be called at this
level.
Safe level 3 is an intermediate step toward level 4 and is not commonly used.
This level extends safe level 3 by preventing any
modifications to trusted objects (including making trusted objects
untrusted). In Ruby 1.8, trusted objects are untainted objects,
which means that any tainted objects can be modified by untrusted
code. In Ruby 1.9, trust and taint are separate, and trusted objects
are those created at safe level 2 or lower. This means that
untrusted code can only modify objects created at safe level 3 or 4,
and also any objects that have been explicitly opened up to
modifications with the untrust
method. This effectively creates a sandbox in which untrusted code
can be run without doing any harm. (In theory, at least—bugs in the
implementation or deficiencies in the underlying security model may
be found in the future.)
Calling eval
on a tainted
string is prohibited in levels 1, 2, and 3. In safe level 4, it is
allowed again because the restrictions on level 4 are stringent
enough that the evaluated string can do no harm. Here is a way to
evaluate arbitrary code in a level-4 sandbox:
def safe_eval(str) Thread.start { # Start sandbox thread $SAFE = 4 # Upgrade safe level eval(str) # Eval in the sandbox }.value # Retrieve result end
In safe level 4, you may not use require
to load another file of Ruby code.
You can use load
, but only in
wrapped form, with true
as its
second argument. This causes Ruby to sandbox the loaded file in an
anonymous module so that any classes, modules, or constants it
defines do not affect the global namespace. This means that code
running under safe level 4 can load, but cannot use, classes and
modules defined in external modules.
You can further restrict a level-4 sandbox by placing the
sandbox thread (before setting $SAFE
) into a ThreadGroup
and calling enclose
on that group. See Listing Threads and Thread Groups for details.
As part of the sandbox it creates, safe level 4 prohibits additional operations including the following:
require
, unwrapped
load
, autoload
, and include
Modifying Object
class
Modifying untainted classes or modules
Metaprogramming methods
Manipulating threads other than current
Accessing thread local data
Terminating the process
File input/output
Modifying environment variables
Seeding the random number generator with srand
18.118.2.240