What's the Purpose of Tainting Ruby Objects

What are the Ruby's Object#taint and Object#trust methods?

taint and trust are part of Ruby's security model. In Ruby, each object has a few flags that it carries around with it, two of which are the Trusted flag and the Tainted flag. How these flags are acted on depends on something called the safe level. The safe level is stored in $SAFE.

Each thread and fiber in a program can have its own safe level. Safe levels range from 0 through 4, with 0 enforcing no security and 4 enforcing so much it should only be used when you're evaling code. You can't assign a lower value to $SAFE than it already has. Also, on UNIX systems where a Ruby script runs as setuid, Ruby automatically sets the safe level to 1.

Tainting

When a object has it's tainted flag set, that means, roughly, that the object came from an unreliable source and therefore can't be used in sensitive operations. When the safe level is 0, the taint flag is ignored (but still set, you can pay attention to it if you want). There are a few methods related to tainting:

  • taint -- Make an object tainted. You can taint an object on all levels with the exception of safe level 4.
  • tainted? -- Check if an object is tainted.
  • untaint -- Remove tainting from an object. This can only be used in safe levels 0, 1, and 2.

Here's an example from the pragprog pickaxe (source) that shows tainting:

# internal data
# =============
x1 = "a string"
x1.tainted? → false
x2 = x1[2, 4]
x2.tainted? → false
x1 =~ /([a-z])/ → 0
$1.tainted? → false
# external data
# =============
y1 = ENV["HOME"]
y1.tainted? → true
y2 = y1[2, 4]
y2.tainted? → true
y1 =~ /([a-z])/ → 1
$1.tainted? → true

To summarize, you can't use dangerous methods on tainted data. So if you do this in safe level 3, you'd get an error:

eval(gets)

Trust

Trust is a lot simpler. Trust has to do with whether the object came from a trusted or untrusted source -- basically, whether it came from anything less than safe level 4, or safe level 4. I'm not sure exactly what effect Ruby's trust has, but take a look here:
http://www.ruby-forum.com/topic/1887006 .


Here are some more resources:
http://phrogz.net/ProgrammingRuby/taint.html -- Some great stuff on safe levels, but I think it's from 1.8 -- there is an updated version for 1.9, just only in the printed version of the book.

http://www.ruby-forum.com/topic/79295 -- On whether safe is safe enough.

What's the purpose of tainting Ruby objects?

It used to be a pretty standard practice when writing CGIs in Perl. There is even a FAQ on it. The basic idea was that the run time could guarantee that you did not implicitly trust a tainted value.

What are tainted objects, and when should we untaint them?

What is Tainted?

User input is tainted, by definition. For example:

string = gets
string.tainted?
# => true

You can also manually taint an object.

string = 'Not yet tainted.'
string.tainted?
# => false

(string = 'Explicitly taint me!').taint
string.tainted?
# => true

Why Untaint an Object?

Generally, you would untaint an object only after you validate and/or sanitize it. Untainting an object marks it as "safe" for certain operations that you wouldn't want to run on untrusted strings or other objects, or when your safe level requires an untainted object to perform the desired operation.

Untainting an Object

The easiest way to untaint an object is to call the Object#untaint method on it. For example, if your string variable holds a tainted object, then:

(string = "Let's taint this string!").taint
string.untaint.tainted?
# => false

More About Tainted Objects

You can find out more about tainted objects from the Locking Ruby in the Safe chapter of Programming Ruby.

Couldn't understand the difference between Object#taint and Object#trust in Ruby

Note: As @themarketka pointed out, as of Ruby 2.2.2, trust has been deprecated and made equivalent to tainting.


The difference is rather odd, and not particularly well documented.

NOTE: At $SAFE level 0, none of these markers do anything at all.

Tainting

The concept of tainting is whether an object comes from a trusted source. A string inputed from standard input is tainted, but a string that's just assigned is not. At higher safe levels, various potentially dangerous operations on tainted data are prohibited (throw SecurityException). Operations like eval, system, etc. Additionally, tainting can be inherited from so-called "child" objects:

2.0.0p0 :001 > s = "Hi!"
=> "Hi!"
2.0.0p0 :002 > s.taint
=> "Hi!"
2.0.0p0 :003 > (s + "World").tainted?
=> true

So, if I do something like system("rm -rf #{gets.chomp}") (DO NOT EXECUTE) at a higher safe level, Ruby will complain as the combination of my untainted string ("rm -rf #{...}") and a tainted string (gets.chomp) creates a tainted string.

Trust

Trust is, unlike tainting, applicable to code, and objects. All running code is either trusted, or untrusted, and all objects are either trusted or untrusted. Untrusted code can only modify untrusted objects. Untrusted code can only create untrusted objects. Code and objects created at safe levels 0-2 are trusted, but anything running or created at $SAFE level 3 or 4 is untrusted, and can only modify untrusted objects.

The Difference

The difference between tainting and trusting is subtle. Tainting is all about what operations you can conduct on data, but trust is about what data you can access. They protect different parts of the system. Additionally, while tainting always exists, and tainted objects can exist at any safe level, trust only comes into play at the so-called "sandboxing" $SAFE levels 3 and 4 which are almost exclusively used for sandboxing external code.

Ruby tainted object returns not tainted string from to_s

I would override Person#taint:

class Person
def initialize(name)
@name = name
end

def taint
@name.taint
super
end
end

ruby, purpose of string replace method

You're correct that this has something to do with pointers. s = "world" would construct a new object and assign s a pointer to that object. Whereas s.replace "world" modifies the string object that s already points to.

One case where replace would make a difference is when the variable isn't directly accessible:

class Foo
attr_reader :x
def initialize
@x = ""
end
end

foo = Foo.new

foo.x = "hello" # this won't work. we have no way to assign a new pointer to @x
foo.x.replace "hello" # but this will

replace has nothing in particular to do with taintedness, the documentation is just stating that it handles tainted strings properly. There are better answers for explaining that topic: What are the Ruby's Object#taint and Object#trust methods?

How do you mark a Ruby Binding as trusted?

You should taint the binding by calling the taint method.

The $SAFE levels are a feature of Ruby that denies certain actions depending on the current level and whether an object is tainted. Tainted strings are assumed to originate from an untrusted source, such as a file, a database, a HTTP client, etc.

At $SAFE level 1, for example, Ruby will not allow you to require files if the argument is a tainted string.

$SAFE level 4 is the most extreme. Ruby will effectively disallow you to modify any nontained object. The idea is that you can use a lower $SAFE level in your application, and instantiate a thread or proc with $SAFE level 4. Within this sandbox, you can modify tainted objects only.

ERB uses this mechanism to allow you to run a template within a sandbox. If you try to get the result of a rendered template from a certain binding, this is what happens:

class TemplateContext
def name; "Teflon Ted"; end
end

template_binding = TemplateContext.new.send(:binding)
ERB.new("Hi, <%= name %>!", 4).result(template_binding)

#=> SecurityError: Insecure: can't modify trusted binding

Blam! This is Ruby telling you that it is not okay to modify a nontainted object at $SAFE level 4. It will not allow you to call eval with the given binding (which is exactly what ERB attempts).

Instead, you should provide the sandbox with a tainted binding. You are explicitly telling Ruby that it is okay to use this binding in a sandbox, and that it should not be trusted outside the sandbox.

class TemplateContext
def name; "Teflon Ted"; end
end

# Binding must be tainted!
template_binding = TemplateContext.new.send(:binding).taint
ERB.new("Hi, <%= name %>!", 4).result(template_binding)

#=> "Hi, Teflon Ted!"

For more information about Ruby's $SAFE level, see the excellent description in the Pickaxe book.



Related Topics



Leave a reply



Submit