CSC/ECE 517 Fall 2009/wiki1a 10 wolf27-Manhattan
Ruby and Java from a Security Perspective
Students: wolf 27 and Manhattan
Introduction
It is widely held that the three “pillars” of a secure system are confidentiality, availability, and integrity. That is, is a users data safe from unauthorized viewing/disclosure; is the users data available when they want it; and is the data the same as what they expected. There are many techniques, algorithms, and methods available to ensure these three pillars. The language used to develop the software on a secure system can play a major role in how effective in how algorithms, etc. help protect the confidentiality, availability, and integrity of secure data. This article compares two popular languages, Ruby and Java with respect to some of features of each language that aid in developing a secure software system.
Ruby
“Safe Levels” in Ruby
Arguable one of Ruby’s features with respect to security is its “Safe Levels”. Ruby implements five levels of data checking to help prevent what its calls “tainted” variables from being used in other parts of the code where the system could be exploited.
All objects in Ruby are marked as “tainted” if they are derived from some external source. For example, an object is tainted if it read using ‘gets’, read from a file, is an environment variable, etc. Each Ruby object contains a method named, ‘tainted?’ as well as methods named ‘taint’ and ‘untaint’.
The ‘tainted?’ method is used to return a boolean value to indicate if the variable is tainted or not. Likewise, the ‘taint’ method is used to taint an untainted object and ‘untaint’ is used to remove a tainted label from an object. The code below shows a simple example of a String object becoming tainted.
#Tainted vs. Untainted >> my_string = String.new # A new string is created => "" >> my_string.tainted? # The string is not tainted; there has been no external influence => false >> my_string = "Hello" # Still no external influence => "Hello" >> my_string.tainted? => false >> my_string = my_string + gets #Since part of the string now came from the console, it is tainted World! => "Hello World!\n" >> my_string.tainted? => true
To help manage tainted and untainted objects, Ruby define a built in constant named $SAFE that allows the user to define what “safe level”. The $SAFE variable is simply set at the beginning of the program in the way any other variable would be. For example,
$SAFE = 3
sets Ruby’s “safe level” to 3.
Each safe level allows (or disallows) certain actions, primarily based on whether an object is tainted or untainted. There are a few other operations that are also disallowed by Ruby at the various safe levels. Note that Ruby’s default safe level is 0, which means there all operates are allowed regardless of the the tainted status of the object. The books “Programming Ruby” [1] provides an excellent table to use as reference for the actives allowed and disallows at each safe level. Below are some highlight of each safe level.
$SAFE = 0 Default “Safe Level” No additional security features All operations allowed on all untainted and tainted variables $SAFE >= 1 Calling the ‘glob’ or ‘eval’ methods on tainted strings is disallowed. Loading a file using a string that is tainted is disallowed. Executing system commands using a tainted string is disallowed. $SAFE >= 2 Loading a file from a source that is writable by ‘world’ is disallowed. $SAFE >= 3 All object created are tainted and no object may not be untainted using the untaint method. $SAFE >= 4 Essentially creates a Sandbox (an isolated, safe, place to run untrused code) Writing to files is disallowed Modifying global variable is disallowed.
Ruby’s Unbounded Polymorphism (a.k.a. Duck Typing) and Security
Ruby’s unbounded polymorphism functionally is arguably very powerful. However this power functionally could also create a potential problem with code developed. Since Ruby is a dynamically typed language, it is not possible to check and ensure that objects being used are of the expected type. If that object looks like another object, it can be treated as that object.
If not managed properly, an adversary or malicious piece of code could be executed unintentionally because of Ruby’s unbounded polymorphism. Consider the code below.
Class Good_code def good_method ... # some good code end
def another_good_method ... # some more good code end end
Class Bad_code def good_method ... #some BAD code, that looks good end
def another_good_method ... #some more bad code end end
def my_method(good_code) good_code.good_method good_code.another_good_method end
my_method(Bad_code.new)
In the example above, the malicious code will run, even though the developer of my_method thought and intended to only run code from the ‘good_code’ object. The developer must take greater caution when implementing code that takes another objects as to unintentionally execute a different object.
Automatic Bounds Checking Ruby automatically checks bounds of data structures such as arrays. Bounds checking is very important to creating a secure system. If a user, intentionally or unintentionally, is able cause code to access an array index outside of the array, the system may crash, other data in memory may be overwritten, or unexpected (possibly private) data may be returned.
Cryptographic Libraries The ability to perform basic cryptographic functions is necessary to help ensure the confidentially of data stored on a system (thought encryption) and the integrity of data (by cryptographic signature/hash). Ruby provides, built in, only a very limited set of cryptographic functionally. One such function is the String classes crypt method.
There are several open source library for performing cryptographic functions on objects available for Ruby such as crypt [2]. The crypt library provides access to such encryption algorithms AES, Blowfish, and IDEA.
Survey of Current and Past Publish Security Vulnerabilities The official Ruby site [3] contains a security section that list a number of current and past security vulnerabilities found.