From: Brian Candler Date: 2003-08-12T08:35:10+09:00 Subject: Re: $SAFE = 5 and Safe Ruby Misleading? On Tue, Aug 12, 2003 at 08:14:53AM +0900, kirindave@lensmen.net wrote: > With all this talk of duck typing and such, I got to thinking about some of my > code that I *thought* executed untrusted code relatively safely. I'd run code > in a safe 5 thread, let it return a string, which I checked to make sure it > was a "string." However, I now realize that concept is totally meaningless. > > Unless I'm misunderstanding, doesn't this mean that there is no such thing as > an object you can "trust" in an absolute sense in Ruby. So, with that in mind, > someone could return an object that, to me as the receiver, looks like a > string, but its actual purpose is to iterate over every object in the runtime, > looking for database like objects, with the purpose of destroying them. Or it > could try and erase as much as it could. > > I know these objects come out tainted, but in order to use them, we need to > untaint them. How do I know that that .upper! isn't actually going to destroy > me? You're right in one sense - if someone has rights to modify core objects and classes in your Ruby environment, then you're toast whatever way you look at it. However the assumption is that these tainted objects come from an external, non-Ruby environment, and typically this means they come in as objects of a fixed type, such as String. (Examples: data taken from ENV, data taken from stdin, data taken from CGI GET or POST parameters) So someone cannot send you a general Ruby object, unless you were to explicitly unmarshal it, and they cannot directly touch your Ruby run-time environment. If they could, they could redefine ENV to return whatever they like, or they could simply override methods in your classes to do whatever they liked. But given that you have (say) a string from an untrusted source, you can untaint it like: foo = ENV['HTTP_HOST'] if /\A[a-zA-Z0-9.-]+\z/ =~ foo foo.untaint else raise "Oi! No!" end The idea being that maybe you want to do something like system("nslookup -q=a #{foo}") but clearly certain values of foo are dangerous, like "; rm -rf /*" The tainting mechanism just forces you to think about what values of a variable may or may not be safe before you use them in system calls, filenames, and such like. BEWARE: Don't use /^...$/ in this case, use /\A...\z/ irb(main):001:0> evil = "foo\nrm -rf /*" => "foo\nrm -rf /*" irb(main):002:0> evil =~ /^foo$/ => 0 irb(main):003:0> evil =~ /\Afoo\z/ => nil Regards, Brian.