Security

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.

Tainted Data

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.

Restricted Execution and Safe Levels

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

Safe level 0

Level 0 is the default safe level. No checks are performed on tainted data.

Safe Level 1

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

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

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.

Safe level 4

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

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.178.165