Bryan Lee
Bryan Lee

Musings about growth, software engineering, and tech.

Ruby on Rails

The Dangers Of Constantize In Rails

Metaprogramming in Ruby / Rails is so sweet. In one of my core projects, I have utilised Rails’ constantize method to dynamically call classes. Let’s say we are keeping a record of people interested in adopting a pet and the pets available for adoption.

Person
   - id
   - name
   - dream_pet (tiger, lion, snake, wolf)

Tiger
Lion
Snake

Hypothetical data model above. Using single table inheritance will be a better solution, but let’s just say we are forced to use this data model and we want to find the number of available pets for a particular individual. This is where constantize comes in very handy:

person = Person.create(name: 'Lola', dream_pet: 'tiger')
count = person.dream_pet.classify.cosntantize.all.count

Using the dream_pet attribute, we dynamically call the correct class so that we can do a count. Convenient.

Eval VS Constantize

While doing a security check on said Rails project yesterday, this came up: Unsafe reflection method constantize called with parameter value. Wait, what? Everyone probably knows the absolute dangers of using eval, allowing potential hackers to exploit and run any ruby code. constantize has been considered the much safer version hence. What’s going on here?

A quick google search pulls up quite a few blog posts about this subject. The summary of it is that while constantize prevents any ruby code from being executed, it allows unintended classes to be initiated, and depending on how your code, the results can be a minor annoyance to a security exploit.

Whitelist That Class

The best solution of course would be to not use constantize at all. However, you might be giving up some good convenience isn’t it?

In my case, it’s pretty easy to whitelist the classes being dynamically called. You could do a simple check like so:

person = Person.create(name: 'Bad Person', dream_pet: 'admin')

PETS_LIST = %w(tiger lion snake dog cat)

if PETS_LIST.include?(person.dream_pet)
    ...
end

In my case, this simple check did the job well enough. Gavin Miller has another take on a whitelist solution that covers a wider range of use cases too.

    comments powered by Disqus