The String#constantize
method is a feature that makes rails fun to code with. This method converts a string to the constant that the string contains (or throws a NameError
if there is no such constant). It makes it easy to store class-types in databases as strings and to code controllers that work with classes of the same duck type.
Most people know that eval
on user data is dangerous, but noboddy seems to care about constantize
. Beware, it is also dangerous and you should constantize with care!
If you want to turn a string into the corresponding class, you could just use eval
:
irb(main):001:0> s="Float" => "Float" irb(main):002:0> klass= eval(s) => Float
But everybody knows that this is dangerous since you can embed arbitrary ruby-code into the string which might do nasty things to your system. Rails defines the String#constantize
method which is less dangerous. But it is not save by any means. Consider the following controller:
def vulnerable_basic_data_new klass=params[:class] obj=klass.constantize.new(params[:form]) #... end
The class
parameter is supposed to be Guest, User or Admin. The posted form is supposed to contain a hash of data that initializes the common attributes of them. Using routes we could call http://myapp/vulnerable_basic_data_new/User/
. That’s nice. A google search reveals that quite a lot of people write code like above .
But wait! What happens if we open http://myapp/vulnerable_basic_data_new/AnyClass/?form=somestring
? Then the application will execute AnyClass.new "somestring"
(if AnyClass exists). We just injected a class into the application that the programmer didn’t expect. A lazy programmer might argue that an attacker cannot control the method that is called, just the class. It is true, that the usual methods new
and find
don’t sound too dangerous. But what if we called create
? And don’t forget that we can call any class that is installed on the system. Thanks to rails autoloading capabilities. So if you accidently installed MiniMagick which happens to have security issue right now in version 1.2.3, we can exploit it:
http://myapp/vulnerable_basic_data_new/MiniMagic::Image/?form=-%20%7C%20xclock
.
This will execute MiniMagic::Image.new("- | xclock")
which will launch a nice xclock on your server. We could have done worse…. Ooooops!
So we should constantize with care. For example, we could use the following method:
class String def constantize_with_care(list_of_klasses=[]) list_of_klasses.each do |klass| return self.constantize if self == klass.to_s end raise "I'm not allowed to constantize #{self}!" end end
And rewrite above controller code to:
def vulnerable_basic_data_new klass=params[:class] obj=klass.constantize_with_care([Guest,User,Admin]).new(params[:form]) #... end
There is a even more sopphisticated constantize_with_care plugin that you can download to use in your own projects.
P.S. In the above google search, I found a single person (Michael Schuerig) warning that using constantize carelessly is dangerous and a single project that tries to protect against class injection. Their safe_to_instantiate?
makes me think about checking for class relationships in constantize_with_care
too.
Update:
I have to apologize to Lisa Seelye. She constantizes params[:controller]
which should be sanitized by the routing to contain only very few, save possibilites. At least as long as she does not create a MiniMagickController
.
Update: I also have some notes on Constantize
in Python.
Update: Gabriel Quadros wrote a nice post about Exploiting Unsafe Reflection in Ruby/Rails Applications.