/apr 5, 2016

Rails Engines: Magic or Curse?

By Jason Yeo

Most Rails applications typically use a bunch of gems. Some of these gems may be Rails engines. Devise, Shoppe and RailsAdmin are examples of engines. The simple definition of an engine is a mini Rails application. When you include an engine in your Rails application, you are actually including an application in your application. Unlike gems that provide simple library code like Faker or Haikunator, an engine may add Rails models, controllers, views, initializers and even routes to your application. The astute would be able to point out how insecure this might be. What's worse is that sometimes these engines are gems that are outside of your control and a static analysis vulnerability scanner Brakeman might not be able to catch such vulnerabilities in them.

Examples of Vulnerabilities in Engines

A simple example would be the CSRF vulnerability that I have found in the Administrate gem. What I've found is that the Administrate gem adds an /admin route for the administrative dashboard but fails to turn on Rails' CSRF protection for those routes. So non-GET requests on /admin/* on your Rails application are not verified for CSRF token authenticity.

After finding this particular vulnerability in Administrate, I went ahead to find out if this vulnerability is widespread and affecting Rails engines in general. Similar to what we did when we were researching the Handlebars vulnerability.

To my surprise, I found this similar vulnerability in other gems like Typus, Upmin and Doorkeeper too. Similarly, these gems add routes to your application and fail to turn on CSRF protection on them. So it would seem that it is somewhat common for open source engines to leave out the protect_from_forgery call.

To make matters worse, the controllers that handle these routes do not reside in your application's directory, hence Brakeman would not be able to pick it up.

What Can You Do About It?

Here's a few things that I would recommend a Rails developer to do if you're concerned about your application's security.

Understand Your Gems

Yes, it's time to stop trusting in the magic of your gems. While it's great that gems offer us a form of abstraction and allow us to focus on writing our own application code, we have become too spoiled into trusting every single gem on our Gemfile. It is not enough to simply understand what functionality the gem offers, we have to also understand what it does to our applications. This is especially true for engines. So, what do you have to look out for when you're inspecting a particular gem?

  1. Find out what routes does the gem adds. You would want to be aware of additional routes that are added to your application. This is typically found in a gem's config/routes.rb file.
  2. If the gem offers a generator to generate code, spend some time to understand and eyeball the generated code. The generate command typically tells you what files were added or changed when the generator is ran.

    -> % rails generate administrate:install
    Running via Spring preloader in process 2458
    route  namespace :admin do
      resources :products
        root to: "products#index"
    end
    
    create  app/controllers/admin/application_controller.rb
    create  app/dashboards/product_dashboard.rb
    create  app/controllers/admin/products_controller.rb
    
  3. Find out the dependencies of the gem. Bundler provides a bundle viz command that visualizes the dependency graph of your application's dependencies.

Which brings me to the next point.

Kill Your Dependencies

If the functionality you need from the gem is simple enough, implement it yourself. Additional dependencies increases your memory usage and enlarges your attack surface. I'm not advocating a NIH mentality here but what I am saying is that you should only use gems if you really need it.

The npm left-pad saga has left me wondering if we are depending too much on our gems. Code reuse is great but if the gem or library is just 10 lines of code long, I think we have gone too far.

Related Posts

By Jason Yeo

Jason is a software engineer at Veracode working on SourceClear's Agent team.