Code Security – Why the US Government Disallows Dynamic Languages for Secure Projects

code-securitydynamic-languagesgovernment

I know some people that are currently working on a project for the US military (low security level, non-combat human resources type data).

An initial state of the project code was submitted to the military for review, and they ran the program through some sort of security analyzer tool. It returned a report of known security issues in the code and required changes that needed to be implemented before delivery of the final product.

One of the items that needed to be resolved was removal of part of the project that was written in Ruby as it is a dynamic language.

What is the background/reason for not allowing a dynamic language to be used in a secure setting? Is this the government being slow to adopt new technologies? Or do dynamic languages pose an additional security risk compared to static languages (ala C++ or Java)?

Best Answer

There are a number of 'neat' things that can be done in dynamic languages that can be tucked away in parts of the code that aren't immediately obvious to another programmer or auditor as to the functionality of a given piece of code.

Consider this sequence in irb (interactive ruby shell):

irb(main):001:0> "bar".foo
NoMethodError: undefined method `foo' for "bar":String
        from (irb):1
        from /usr/bin/irb:12:in `<main>'
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> "bar".foo
=> "foobar!"

What happened there is I tried to call the method foo in a String constant. This failed. I then opened up the String class and defined the method foo o return "foobar!", and then called it. This worked.

This is known as an open class and gives me nightmares every time I think of writing code in ruby that has any sort of security or integrity. Sure it lets you do some neat things quite fast... but I could make it so every time someone stored a string, it stored it to a file, or sent it over the network. And this little bit of redefining the String can be tucked anywhere in the code.

Many other dynamic languages have similar things that can be done. Perl has Tie::Scalar that can behind the scenes change how a given scalar works (this is a bit more obvious and requires a specific command that you can see, but a scalar that is passed in from somewhere else could be a problem). If you have access to the Perl Cookbook, look up Recipe 13.15 - Creating Magic Variables with tie.

Because of these things (and others often part of dynamic languages), many approaches to static analysis of security in code doesn't work. Perl and Undecidability shows this to be the case and points out even such trivial problems with syntax highlighting (whatever / 25 ; # / ; die "this dies!"; poses challenges because the whatever can be defined to take arguments or not at runtime completely defeating a syntax highlighter or static analyzer).


This can get even more interesting in Ruby with the ability to access the environment that a closure was defined in (see YouTube: Keeping Ruby Reasonable from RubyConf 2011 by Joshua Ballanco). I was made aware of this video from an Ars Technica comment by MouseTheLuckyDog.

Consider the following code:

def mal(&block)
    puts ">:)"
    block.call
    t = block.binding.eval('(self.methods - Object.methods).sample')
    block.binding.eval <<-END
        def #{t.to_s}
          raise 'MWHWAHAW!'
        end
    END
end

class Foo
    def bar
        puts "bar"
    end

    def qux
        mal do
            puts "qux"
        end
    end
end

f = Foo.new
f.bar
f.qux

f.bar
f.qux

This code is fully visible, but the mal method could be somewhere else... and with open classes, of course, it could be redefined somewhere else.

Running this code:

~/$ ruby foo.rb 
bar
>:)
qux
bar
b.rb:20:in `qux': MWHWAHAW! (RuntimeError)
    from b.rb:30:in `'
~/$ ruby foo.rb 
bar
>:)
qux
b.rb:20:in `bar': MWHWAHAW! (RuntimeError)
    from b.rb:29:in `'

In this code, the closure was able to access all of the methods and other bindings defined in the class at that scope. It picked a random method and redefined it to raise an exception. (see the Binding class in Ruby to get an idea of what this object has access to)

The variables, methods, value of self, and possibly an iterator block that can be accessed in this context are all retained.

A shorter version that shows the redefinition of a variable:

def mal(&block)
    block.call
    block.binding.eval('a = 43')
end

a = 42
puts a
mal do 
  puts 1
end
puts a

Which, when run produces:

42
1
43

This is more than the open class that I mentioned above that makes static analysis impossible. What is demonstrated above is that a closure that is passed somewhere else, carries with it the full environment that it was defined in. This is known as a first class environment (just as when you can pass around functions, they are first class functions, this is the environment and all of the bindings available at that time). One could redefine any variable that was defined in the scope of the closure.

Good or bad, complaining about ruby or not (there are uses where one would want to be able to get at the environment of a method (see Safe in Perl)), the question of "why would ruby be restricted in for a government project" really is answered in that video linked above.

Given that:

  1. Ruby allows one to extract the environment from any closure
  2. Ruby captures all bindings in the scope of the closure
  3. Ruby maintains all bindings as live and mutable
  4. Ruby has new bindings shadow old bindings (rather than cloning the environment or prohibiting rebinding)

With the implications of these four design choices, it is impossible to know what any bit of code does.

More about this can be read at Abstract Heresies blog. The particular post is about Scheme where such a debate was had. (related on SO: Why doesn't Scheme support first class environments?)

Over time, however, I came to realise that there was more difficulty and less power with first-class environments than I had originally thought. At this point I believe that first-class environments are useless at best, and dangerous at worst.

I hope this section shows the danger aspect of first class environments and why it would be asked to remove Ruby from the provided solution. Its not just that Ruby is a dynamic language (as mentioned else-answer, other dynamic languages have been allowed in other projects), but that there are specific issues that make some dynamic languages even more difficult to reason about.

Related Topic