Preventing SQL injections in Ruby (and other vulnerabilities)

Ruby is a wonderful language for beginner coders to start with and scale to large, distributed Web and Desktop applications. It has an accepting and helpful community and strives to keep itself up to date to match the needs of developers.

You can add functionality to your Ruby application with gems that introduce more complexity and possibilities for exploitation into an application (especially the infamous Rails), and whilst the Ruby community had high profile security issues in the past (SQL Injections to Cross-site scripting – XSS), if you follow best practices, use recommended methods, and keep your versions up to date, you should avoid a lot of them.

Escaping and sanitizing user input

To prevent potentially harmful input being unintentionally executed or finding it’s way to your database, Ruby (and Rails) offer comprehensive escaping methods that it’s essential you use.

The are multiple ways to sanitize and escape potentially harmful input in Ruby (and Rails), and Rails version 3 and above will escape content automatically:

<%= @user.biography %>

Which is equivalent to prefixing html_escape in earlier Rails versions and will escape any HTML to make it less harmful (but more unsightly). There are times when you still want to call the html_escape method anyway just to be sure.

<p>I am a writer and blogger. !<script>alert(document.cookie);</script></p>

This takes you so far, if you want to be extra cautious, then you can use the Rails sanitize method to apply filters that Rails considers safe. This includes encoding tags and stripping unallowed attributes:

<%= sanitize @user.biography %>

Great, but what if you want to allow HTML, for example, to allow users to style the text they input? Helpfully Rails allows you to add a list of acceptable tags to your application.rb file:

config.after_initialize do
  ActionView::Base.sanitized_allowed_tags = 'b', 'i'
end

Or you can add and remove tags as you need them, for example:

config.after_initialize do
  ActionView::Base.sanitized_allowed_tags += 'a'
  ActionView::Base.sanitized_allowed_tags.delete 'b'
end

It can be easy to forget about escaping HTML when you want to generate a custom template, for example, if you use the below, malicious code could still sneak in:

module BioHelper
  def block_bio(user)
    "<aside>#{user.biography}</aside>".html_safe
  end
end

To be safe, make sure you strip it before setting it as valid HTML:

module BioHelper
  def block_bio(user)
    "<aside>#{html_escape user.biography}</aside>".html_safe
  end
end

Or use the methods Rails provides for generating HTML:

module BioHelper
  def block_bio(user)
    content_tag(:aside, user)
  end
end

Databases

One of the most powerful aspects of Rails is it’s database abstraction it offers in the form of ActiveRecord for interacting with MySQL, MariaDB, PostgreSQL and SQLite databases and not having to worry about the underlying technology. Whilst Rails tries hard to prevent vulnerabilities and opportunities for SQL injection, there are still precautions you can take.

The Rails SQL injection site does such a good job of explaining potentially harmful methods, including a repository of all examples that I suggest you head over there for a comprehensive rundown. In summary, there are ActiveRecord methods (depending on the Rails version) that allow for arbitrary SQL and you should either prevent user input into these or sanitize the input to ensure that only values you want make it to the database.

A common potential gap for injections to sneak in is searching records, for example:

def index
  @writers = Writer.all
  unless params[:query].blank?
    @writers = @writers.where("biography like '%#{params[:query]}%'")
  end
end

Whilst this gives users a flexible search option, it does mean that they can put anything into the search query, including potentially harmful content. It’s a well-known potential vulnerability in Rails, and the solution is to instead use an array, which will sanitize the input:

def index
  @writers = Writer.all
  unless params[:query].blank?
    @writers = @writers.where("biography like ", params[:query])
  end
end

Object serialization

This vulnerability was a particularly serious one posted to the Rails security list in 2013 and was resolved in newer versions, so check that you are running 3.2.11, 3.1.10, 3.0.19, 2.3.15 and above.

Following design ideas across the Rails project, you are able to import YAML, JSON and CSV files, and Rails will dump them into strings and attempt to create objects from them. As with the points above, strings can be harmful if not escaped properly and earlier versions did not sufficiently escape these strings before creating objects out of them. If you are unsure if your Rails version is sufficiently up to date, then the tools outlined later in this article will identify potential serialization issues for you.

Insecure gems

Gems are a wonderful source of functionality to extend your ruby applications, but signing them to confirm identity is not a compulsory step, and there is no guarantee of trust, exposing you to code from unknown sources and of unknown quality. If you are a gem developer, then read the gem security guide to see how you can assure users better. If you’re not a gem developer, then encourage other developers to do the same and only use gems you trust and have been able to find information about. This GitHub repository is also a good source of information on gems that the Ruby community considers to have security problems.

Identifying vulnerabilities

The first steps to preventing most problems with code are to create a checklist of potential issues and check for their hopeful absence. This can be a part of your testing regime or a step before testing. Ideally, you should ask someone else to check your code, as spending hours staring at the same lines can cause ‘code-blindness’ and you may not notice small details anymore. Naturally, this is not a new process and there are numerous tools to help you such as Crucible, Upsource or both GitHub and GitLab are adding features to help with the process.

Ideally, you should test with as many methods as possible, as in my opinion checking application code too much is not possible. If you are looking for guidance on whether you should trust a tool or not, then read forums, or the OWASP (Open Web Application Security Project) site, which is hard to navigate, but a great source of information.

Code analysis

As an opinionated language, Ruby has well established coding standards documented in the community-driven Ruby style guide. This is purely a guide, but you are strongly advised to follow it and covers much more than potential code vulnerabilities.

If checking your code manually to match the guide sounds like a daunting task, then there are several automated options, depending on what aspect of your code you want to check.

Linters provide guidance on code best practices and are part of the domain of static application security testing (SAST) tools that analyze your code, whereas dynamic application security testing (DAST) tools involve operational testing, i.e. how the application actually works and how people might try to ‘break’ it. You can use these tools manually as part of your editor or local development process, or as part of an automated testing and deployment process

  • Rubocop (SAST) – A static code analyzer based on the Ruby style guide that reports potential issues in your code and will also attempt to fix some of them.
  • Reek (SAST) – Doesn’t claim to lint code, but instead report any potential ‘code smells‘ that may cause harm to your code.
  • Brakeman (SAST) – Specifically aimed at analyzing potential security issues in your Ruby on Rails code, including the vulnerabilities mentioned in this post, and more. You can run it periodically over your code base with brakeman command, or even better, hook it up to continuous integration for code commits.
  • Arachni and OWASP Zed Attack Proxy (DAST) – Both are entire frameworks that help penetration testers and security administrators investigate the security of web applications. Arachni has a Ruby library, but neither are limited to Ruby applications.

Ruby IDEs such as RubyMine, Aptana, and text editors such as Atom and Sublime Text typically have these tools available as plugins.

Ride the Rails responsibly

Thankfully for developers, the dark days of major Ruby security issues are behind us, and as long as you follow the best practices touched on in this post, you can sleep soundly at night. Of course, with the more obvious issues fixed, hackers will be looking harder for more subtle ways into your application. Keep yourselves up to date with new vulnerabilities by using the services noted above, reading the Rails security page and the Rails security mailing list.

You can also actively protect your application by using Sqreen by just adding a small gem to your application. It will notify you of any vulnerabilities as they happen and block most common attacks, giving you time to fix them.

About the Author

I explain cool tech to the World. I am a Technical Writer and blogger. I have crazy projects in progress and will speak to anyone who listens. Follow me on Twitter or checkout my website.