SafeYAML

The SafeYAML gem provides an alternative implementation of YAML.load suitable for accepting user input in Ruby applications. Unlike Ruby's built-in implementation of YAML.load, SafeYAML's version will not expose apps to arbitrary code execution exploits (such as the one recently discovered in Rails).

Installation

Add this line to your application's Gemfile:

gem "safe_yaml"

And then execute:

$ bundle

Or install it yourself as:

$ gem install safe_yaml

Usage

Suppose your application were to contain some code like this:

class ExploitableClassBuilder
  def []=(key, value)
    @class ||= Class.new

    @class.class_eval <<-EOS
      def #{key}
        #{value}
      end
    EOS
  end

  def create
    @class.new
  end
end

Now, if you were to use YAML.load on user input anywhere in your application without the SafeYAML gem installed, an attacker could make a request with a carefully-crafted YAML string to execute arbitrary code (yes, including system("unix command")) on your servers.

Observe:

> yaml = <<-EOYAML
> --- !ruby/hash:ExploitableClassBuilder
> "foo; end; puts %(I'm in yr system!); def bar": "baz"
> EOYAML
=> "--- !ruby/hash:ExploitableClassBuilder\n\"foo; end; puts %(I'm in yr system!); def bar\": \"baz\"\n"

> YAML.load(yaml)
I'm in yr system!
=> #<ExploitableClassBuilder:0x007fdbbe2e25d8 @class=#<Class:0x007fdbbe2e2510>>

With YAML.safe_load, that attacker would be thwarted:

> require "safe_yaml"
=> true
> YAML.load(yaml)
=> {"foo; end; puts %(I'm in yr system!); def bar"=>"baz"}

Notes

The way that SafeYAML works is by restricting the kinds of objects that can be deserialized via YAML.load. More specifically, only the following types of objects can be deserialized by default:

  • Hashes
  • Arrays
  • Strings
  • Numbers
  • Dates
  • Booleans
  • Nils

Additionally, symbols will also be deserialized if the YAML.enable_symbol_parsing option is set to true.

For scenarios where the data to be parsed is from a trusted source and it is still desirable to deserialize arbitrary Ruby objects, the original implementation of YAML.load is available as YAML.orig_load.

Requirements

SafeYAML requires Ruby 1.8.7 or newer and works with both Syck and Psych.