Handling Security Issues In Open Source Projects

Tute Costa

A vulnerability was recently discovered in the doorkeeper gem. It taught me the hard way how to deal with security issues in OSS, and I documented what I’ve learned in the process.

Keep it private at first

When you become aware of a vulnerability in a project you maintain, keep it private. A vulnerability shouldn’t be made public until it’s been fixed.

Rails developers may have experienced this: a new patch version of Rails is suddenly announced, and you should upgrade. What happens in such cases is:

  1. Someone discovers a vulnerability.
  2. Rails core team work with the discoverer to fix it.
  3. A new patch version is released for affected Rails versions.
  4. The vulnerability and how to upgrade is announced in Ruby on Rails Twitter account, their blog, security mailing lists, etc.

Let’s see in detail how all this happens.

Request a CVE identifier

Before you start fixing the bug (or while you are doing it) you should request a “CVE id”. An id can be requested from any of the “CVE Numbering Authorities”. I myself sent my request to RedHat, and Kurt Seifried provided me with an identifier in less than an hour. He hosts a wiki with more information.

CVE stands for “Common Vulnerabilities and Exposures”. This allows us to sanely talk about security issues (“issue CVE-2009-3555” instead of “the OpenSSL vulnerability, from like 2009, the DoS one… no, not that one”). CVE allows multiple vendors, products, and customers to properly track security vulnerabilities and make sure they are dealt with. CVE Identifiers are from an international information security effort that is publicly available and free to use.

Write the accompanying CVE report

The CVE report specifies:

  • The project (name and related links)
  • A description of the vulnerability
  • Affected and fixed versions
  • What’s the vulnerability’s impact (how many people are affected and how)
  • What is the upgrade process
  • What workarounds can users take, if any
  • Credits
  • Any other kind of relevant information you can provide

Here is my example:

Cross-site request forgery (CSRF) vulnerability in doorkeeper 1.4.0
and earlier allows remote attackers to hijack the user's OAuth
autorization code. This vulnerability has been assigned the CVE
identifier CVE-2014-8144.

Versions Affected:  1.4.0 and below
Fixed Versions:     1.4.1, 2.0.0

## Impact

Doorkeeper's endpoints didn't have CSRF protection. Any HTML document
on the Internet can then read a user's authorization code with arbitrary
scope from any Doorkeeper-compatible Rails app you are logged in.

## Releases

The 1.4.1 and 2.0.0 releases are available at
https://rubygems.org/gems/doorkeeper and
https://github.com/doorkeeper-gem/doorkeeper.

## Upgrade Process

Upgrade doorkeeper version at least to 1.4.1.

## Workarounds

There are no feasible workarounds for this vulnerability.

## Credits

Thanks to Sergey Belov of DigitalOcean for finding the vulnerability, Phill
Baker of DigitalOcean for reporting and fixing it, and to Egor Homakov of
Sakurity.com for raising awareness.

Fix it and publish releases

Work on the vulnerability in private. Only publish the fixes when you release new patched versions of your project. This keeps people from learning about the vulnerability before it’s been fixed, potentially taking advantage from affected deploys of your software. The goal is to reach most users of your project so they can upgrade as soon as possible.

If you take too long to release, the attacker might announce it before you have a fix ready. The person who reported the vulnerability is the “white hat”. There may already be “black hats” taking advantage of it. CVE reports typically go public after 2 weeks since an id was granted to address this issue.

Spread the word

After you get the CVE identifier and report, the fix and releases ready, publish this information to security lists and to users of your library, as widely as you’re able to, using any communication techniques available to you.

I was advised to post doorkeeper’s report to the following lists:

After all is done you can relax. Until next time!

One thing end users can do

Adding the report to ruby-advisory-db is particularly useful for end users.

Ruby developers can use bundler-audit, which uses ruby-advisory-db, to automatically alert themselves of security issues. We made bundle-audit a dependency in all our Rails apps by adding it to Suspenders in version 1.19.0.

Here’s how it is set up in our Rails apps:

# Gemfile
group :development do
  gem "bundler-audit"
end

# Rakefile
task default: "bundler:audit"

# lib/tasks/bundler_audit.rake
if Rails.env.development? || Rails.env.test?
  require "bundler/audit/cli"

  namespace :bundler do
    task :audit do
      %w(update check).each do |command|
        Bundler::Audit::CLI.start [command]
      end
    end
  end
end

We hook the rake task into our default test suite so that we are sure it is run often. Running this task in an app using an insecure version of Doorkeeper would print out something like:

Name: doorkeeper
Version: 1.3.0
Advisory: CVE-2014-8144
Criticality: Unknown
URL: https://groups.google.com/forum/#!topic/ruby-security-ann/5_VqJtNc8jw
Title: Cross-site request forgery (CSRF) vulnerability in doorkeeper 1.4.0
and earlier.
Solution: upgrade to ~> 1.4.1, >= 2.0.0

Things not to do

Don’t do what I did with Doorkeeper:

  • Tell the person who reports the vulnerability to send a pull request, which is public.
  • Wait until the next scheduled release to bump the patch version with the fix.

Instead:

  • Keep it private, following the guidelines described above, making it public only after the fix is released.
  • Release the fix as soon as possible.

A week after the Doorkeeper fix, Egor Homakov raised awareness, calling users to upgrade, and myself to finally release. Thank you Egor for your pat on the back that morning and for the ongoing help you are providing.