2013-02-03 11:14:44 +04:00
|
|
|
= Ruby Security
|
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
The Ruby programming language is large and complex and there are many security
|
|
|
|
pitfalls often encountered by newcomers and experienced Rubyists alike.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
This document aims to discuss many of these pitfalls and provide more secure
|
|
|
|
alternatives where applicable.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-06 08:03:39 +04:00
|
|
|
Please check the full list of publicly known CVEs and how to correctly report a
|
2016-09-11 13:33:36 +03:00
|
|
|
security vulnerability, at: https://www.ruby-lang.org/en/security/
|
|
|
|
Japanese version is here: https://www.ruby-lang.org/ja/security/
|
2013-02-06 08:03:39 +04:00
|
|
|
|
2013-05-04 02:21:34 +04:00
|
|
|
Security vulnerabilities should be reported via an email to
|
|
|
|
mailto:security@ruby-lang.org ({the PGP public
|
2016-09-11 13:33:36 +03:00
|
|
|
key}[https://www.ruby-lang.org/security.asc]), which is a private mailing list.
|
2013-05-04 02:21:34 +04:00
|
|
|
Reported problems will be published after fixes.
|
|
|
|
|
2013-02-03 11:14:44 +04:00
|
|
|
== +Marshal.load+
|
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
Ruby's +Marshal+ module provides methods for serializing and deserializing Ruby
|
|
|
|
object trees to and from a binary data format.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
Never use +Marshal.load+ to deserialize untrusted or user supplied data.
|
|
|
|
Because +Marshal+ can deserialize to almost any Ruby object and has full
|
|
|
|
control over instance variables, it is possible to craft a malicious payload
|
|
|
|
that executes code shortly after deserialization.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
If you need to deserialize untrusted data, you should use JSON as it is only
|
|
|
|
capable of returning 'primitive' types such as strings, arrays, hashes, numbers
|
|
|
|
and nil. If you need to deserialize other classes, you should handle this
|
|
|
|
manually. Never deserialize to a user specified class.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 13:49:09 +04:00
|
|
|
== YAML
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 13:49:09 +04:00
|
|
|
YAML is a popular human readable data serialization format used by many Ruby
|
2013-09-07 08:58:40 +04:00
|
|
|
programs for configuration and database persistence of Ruby object trees.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
Similar to +Marshal+, it is able to deserialize into arbitrary Ruby classes.
|
|
|
|
For example, the following YAML data will create an +ERB+ object when
|
|
|
|
deserialized:
|
2013-02-03 11:14:44 +04:00
|
|
|
|
|
|
|
!ruby/object:ERB
|
|
|
|
src: puts `uname`
|
|
|
|
|
2013-02-05 13:49:09 +04:00
|
|
|
Because of this, many of the security considerations applying to Marshal are
|
|
|
|
also applicable to YAML. Do not use YAML to deserialize untrusted data.
|
|
|
|
|
2013-02-03 11:14:44 +04:00
|
|
|
== Symbols
|
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
Symbols are often seen as syntax sugar for simple strings, but they play a much
|
|
|
|
more crucial role. The MRI Ruby implementation uses Symbols internally for
|
|
|
|
method, variable and constant names. The reason for this is that symbols are
|
|
|
|
simply integers with names attached to them, so they are faster to look up in
|
|
|
|
hashtables.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2015-02-04 19:24:16 +03:00
|
|
|
Starting in version 2.2, most symbols can be garbage collected; these are
|
|
|
|
called <i>mortal</i> symbols. Most symbols you create (e.g. by calling
|
|
|
|
+to_sym+) are mortal.
|
|
|
|
|
|
|
|
<i>Immortal</i> symbols on the other hand will never be garbage collected.
|
|
|
|
They are created when modifying code:
|
|
|
|
* defining a method (e.g. with +define_method+),
|
|
|
|
* setting an instance variable (e.g. with +instance_variable_set+),
|
|
|
|
* creating a variable or constant (e.g. with +const_set+)
|
2015-02-06 00:09:27 +03:00
|
|
|
C extensions that have not been updated and are still calling `SYM2ID`
|
2015-02-04 19:24:16 +03:00
|
|
|
will create immortal symbols.
|
2015-02-05 23:06:11 +03:00
|
|
|
Bugs in 2.2.0: +send+ and +__send__+ also created immortal symbols,
|
|
|
|
and calling methods with keyword arguments could also create some.
|
2015-02-04 19:24:16 +03:00
|
|
|
|
|
|
|
Don't create immortal symbols from user inputs. Otherwise, this would
|
2015-02-02 04:51:37 +03:00
|
|
|
allow a user to mount a denial of service attack against your application by
|
|
|
|
flooding it with unique strings, which will cause memory to grow indefinitely
|
|
|
|
until the Ruby process is killed or causes the system to slow to a halt.
|
|
|
|
|
2015-02-04 19:24:16 +03:00
|
|
|
While it might not be a good idea to call these with user inputs, methods that
|
2015-02-04 19:41:38 +03:00
|
|
|
used to be vulnerable such as +to_sym+, +respond_to?+,
|
2015-02-04 19:24:16 +03:00
|
|
|
+method+, +instance_variable_get+, +const_get+, etc. are no longer a threat.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 13:49:09 +04:00
|
|
|
== Regular expressions
|
|
|
|
|
|
|
|
Ruby's regular expression syntax has some minor differences when compared to
|
|
|
|
other languages. In Ruby, the <code>^</code> and <code>$</code> anchors do not
|
|
|
|
refer to the beginning and end of the string, rather the beginning and end of a
|
|
|
|
*line*.
|
|
|
|
|
|
|
|
This means that if you're using a regular expression like
|
|
|
|
<code>/^[a-z]+$/</code> to restrict a string to only letters, an attacker can
|
|
|
|
bypass this check by passing a string containing a letter, then a newline, then
|
|
|
|
any string of their choosing.
|
|
|
|
|
|
|
|
If you want to match the beginning and end of the entire string in Ruby, use
|
|
|
|
the anchors +\A+ and +\z+.
|
|
|
|
|
|
|
|
== +eval+
|
|
|
|
|
|
|
|
Never pass untrusted or user controlled input to +eval+.
|
|
|
|
|
|
|
|
Unless you are implementing a REPL like +irb+ or +pry+, +eval+ is almost
|
|
|
|
certainly not what you want. Do not attempt to filter user input before passing
|
|
|
|
it to +eval+ - this approach is fraught with danger and will most likely open
|
|
|
|
your application up to a serious remote code execution vulnerability.
|
|
|
|
|
2013-02-03 11:14:44 +04:00
|
|
|
== +send+
|
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
'Global functions' in Ruby (+puts+, +exit+, etc.) are actually private instance
|
|
|
|
methods on +Object+. This means it is possible to invoke these methods with
|
|
|
|
+send+, even if the call to +send+ has an explicit receiver.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
|
|
|
For example, the following code snippet writes "Hello world" to the terminal:
|
|
|
|
|
|
|
|
1.send(:puts, "Hello world")
|
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
You should never call +send+ with user supplied input as the first parameter.
|
|
|
|
Doing so can introduce a denial of service vulnerability:
|
2013-02-03 11:14:44 +04:00
|
|
|
|
|
|
|
foo.send(params[:bar]) # params[:bar] is "exit!"
|
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
If an attacker can control the first two arguments to +send+, remote code
|
|
|
|
execution is possible:
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 13:49:09 +04:00
|
|
|
# params is { :a => "eval", :b => "...ruby code to be executed..." }
|
|
|
|
foo.send(params[:a], params[:b])
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
When dispatching a method call based on user input, carefully verify that the
|
|
|
|
method name. If possible, check it against a whitelist of safe method names.
|
2013-02-03 11:14:44 +04:00
|
|
|
|
2013-02-05 05:18:22 +04:00
|
|
|
Note that the use of +public_send+ is also dangerous, as +send+ itself is
|
|
|
|
public:
|
2013-02-03 11:14:44 +04:00
|
|
|
|
|
|
|
1.public_send("send", "eval", "...ruby code to be executed...")
|
2013-02-05 13:49:09 +04:00
|
|
|
|
|
|
|
== DRb
|
|
|
|
|
|
|
|
As DRb allows remote clients to invoke arbitrary methods, it is not suitable to
|
|
|
|
expose to untrusted clients.
|
|
|
|
|
|
|
|
When using DRb, try to avoid exposing it over the network if possible. If this
|
|
|
|
isn't possible and you need to expose DRb to the world, you *must* configure an
|
|
|
|
appropriate security policy with <code>DRb::ACL</code>.
|