diff options
Diffstat (limited to 'doc/security')
| -rw-r--r-- | doc/security/command_injection.rdoc | 15 | ||||
| -rw-r--r-- | doc/security/security.rdoc | 127 |
2 files changed, 142 insertions, 0 deletions
diff --git a/doc/security/command_injection.rdoc b/doc/security/command_injection.rdoc new file mode 100644 index 0000000000..d46e42f7be --- /dev/null +++ b/doc/security/command_injection.rdoc @@ -0,0 +1,15 @@ += Command Injection + +Some Ruby core methods accept string data +that includes text to be executed as a system command. + +They should not be called with unknown or unsanitized commands. + +These methods include: + +- Kernel.exec +- Kernel.spawn +- Kernel.system +- {\`command` (backtick method)}[rdoc-ref:Kernel#`] + (also called by the expression <tt>%x[command]</tt>). +- IO.popen (when called with other than <tt>"-"</tt>). diff --git a/doc/security/security.rdoc b/doc/security/security.rdoc new file mode 100644 index 0000000000..af9970d336 --- /dev/null +++ b/doc/security/security.rdoc @@ -0,0 +1,127 @@ += Ruby Security + +The Ruby programming language is large and complex and there are many security +pitfalls often encountered by newcomers and experienced Rubyists alike. + +This document aims to discuss many of these pitfalls and provide more secure +alternatives where applicable. + +Please check the full list of publicly known CVEs and how to correctly report a +security vulnerability, at: https://www.ruby-lang.org/en/security/ +Japanese version is here: https://www.ruby-lang.org/ja/security/ + +Security vulnerabilities should be reported via an email to +mailto:security@ruby-lang.org ({the PGP public +key}[https://www.ruby-lang.org/security.asc]), which is a private mailing list. +Reported problems will be published after fixes. + +== +Marshal.load+ + +Ruby's +Marshal+ module provides methods for serializing and deserializing Ruby +object trees to and from a binary data format. + +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. + +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. + +== YAML + +YAML is a popular human readable data serialization format used by many Ruby +programs for configuration and database persistence of Ruby object trees. + +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, using the +unsafe_load+ method: + + !ruby/object:ERB + src: puts `uname` + +Because of this, many of the security considerations applying to Marshal are +also applicable to YAML. Do not use YAML to deserialize untrusted data. + +== Symbols + +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. + +Most symbols can be garbage collected; these are called _mortal_ +symbols. Most symbols you create (e.g. by calling +to_sym+) are mortal. + +_Immortal_ 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+) +C extensions that have not been updated and are still calling +SYM2ID+ +will create immortal symbols. + +Don't create immortal symbols from user inputs. Otherwise, this would +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. + +While it might not be a good idea to call these with user inputs, methods that +used to be vulnerable such as +to_sym+, +respond_to?+, ++method+, +instance_variable_get+, +const_get+, etc. are no longer a threat. + +== 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. + +== +send+ + +'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. + +For example, the following code snippet writes "Hello world" to the terminal: + + 1.send(:puts, "Hello world") + +You should never call +send+ with user supplied input as the first parameter. +Doing so can introduce a denial of service vulnerability: + + foo.send(params[:bar]) # params[:bar] is "exit!" + +If an attacker can control the first two arguments to +send+, remote code +execution is possible: + + # params is { :a => "eval", :b => "...ruby code to be executed..." } + foo.send(params[:a], params[:b]) + +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. + +Note that the use of +public_send+ is also dangerous, as +send+ itself is +public: + + 1.public_send("send", "eval", "...ruby code to be executed...") |
