Ideas on "Why Living Dangerous can be A Good Thing" in Ruby?


#1

After my first day back at my University, I was quickly reminded that
not everyone in the world embraces the “You’ll shoot your eye out”
nature of Ruby as much as we all do. I just started a course on
object oriented design in C++ and naturally the issues of security
came up as soon as the fact that I had been working in Ruby had been
mentioned.

Rather than spending an hour arguing for why Ruby’s openness makes
my life easier, I decided that i’d do a little research and digging
around and then form an O’Reilly blog article on the topic. As part
of that research, I’m asking the RubyTalk community for some well
founded opinions that make the case for dynamicity and openness,
particularly the meta-programming tricks many of us have become
acquainted with.

I’d also like to hear places that people would NOT use ruby due to
it’s open nature, and the reasons for that. I’d like this article to
be more a technical piece on why a little bit of life on the wild side
can be a wise decision, and also why it can be less of a dangerous
endeavor than some might believe when done correctly. I’d like to
avoid zealotry and flamage and my meta-programming-fu can kick your
static ass type things, and instead focus on the nuts and bolts of the
issue.

Anything you can offer up would be much appreciated, your experiences
on various projects, resources you’ve found that address this topic,
experiences with ‘secure’ languages who’s benefits did not outweigh
the costs, insight on the benefits on an open design baked into a
language, functionality that would be difficult or impossible to
replicate in a more rigid setting, etc.

My goal is to produce a well formed article that will show the
cautious incomer to Ruby that we’re not simply running with scissors
over here :slight_smile:

I do have a few of my own ideas on the topic, and I will contribute
them for your review and suggestions if they are not brought up by
others. I’d like to base this article heavily on the experiences of
those active in the community, so please do share!

Thanks

-Greg


#2

On Sun, Jan 08, 2006 at 11:28:17AM +0900, Gregory B. wrote:
} After my first day back at my University, I was quickly reminded that
} not everyone in the world embraces the “You’ll shoot your eye out”
} nature of Ruby as much as we all do. I just started a course on
} object oriented design in C++ and naturally the issues of security
} came up as soon as the fact that I had been working in Ruby had been
} mentioned.
}
} Rather than spending an hour arguing for why Ruby’s openness makes
} my life easier, I decided that i’d do a little research and digging
} around and then form an O’Reilly blog article on the topic. As part
} of that research, I’m asking the RubyTalk community for some well
} founded opinions that make the case for dynamicity and openness,
} particularly the meta-programming tricks many of us have become
} acquainted with.
[…]

I’m not clear on what openness we’re talking about. Do you mean one or
more
of the following:

  1. all the source code of an app is visible and can be scanned for
    vulnerabilities

  2. the source code for the interpreter is available and can be scanned
    for
    vulnerabilities

  3. duck typing allows unintended objects to be used in unintended ways

  4. the ability to add/replace methods in existing classes allows library
    internals to be inspected or modified

} Thanks
} -Greg
–Greg


#3

On 1/7/06, Gregory S. removed_email_address@domain.invalid wrote:

I’m not clear on what openness we’re talking about. Do you mean one or more
of the following:

  1. duck typing allows unintended objects to be used in unintended ways

  2. the ability to add/replace methods in existing classes allows library
    internals to be inspected or modified

These two.

Which are considered as features by most, but often as vulnerabilities
by outsiders :slight_smile:


#4

On Jan 8, 2006, at 12:01 AM, Gregory B. wrote:

library
internals to be inspected or modified

These two.

Which are considered as features by most, but often as vulnerabilities
by outsiders :slight_smile:

If there are vulnerabilities, who are the attackers? I think there are
reasonable issues to discuss in this area but I think the language
choice
kind of skews the discussion. Why is the situation characterized as a
‘security’ issue? Why are the contents of a library viewed as some sort
of national secret that must be protected from prying eyes/objects?
What is being ‘protected’? Why? From Whom?

I don’t think C/C++/Java library designers view programmers of client
code as actively hostile, like some sort of foreign agent trying to
sneak
in to commit sabotage, yet it sounds like that is the scenario that
is in
their mind when they ask how Ruby/Python/Perl/Smalltalk/etc can
“protect” against
such problems.

I think it is really just a matter of phrasing the concern in
a different way:

In a statically typed language, the compiler can help to identify
programming errors during the compilation process instead of
at run time.  What tools and/or techniques can be used with Ruby to
identify programming errors before the code is put into production?

Gary W.


#5

Gregory B. wrote:

of that research, I’m asking the RubyTalk community for some well
founded opinions that make the case for dynamicity and openness,
particularly the meta-programming tricks many of us have become
acquainted with.

Here’s a common case for me. I often have some code that needs to
escape or transform a string for some purpose. Rather than write

stuff << NameSpacedUtilClass.some_modifier( my_string )

I prefer

stuff << my_string.some_modifier

because I prefer Tell, Don’t Ask. It tends to make for crisper, clearer
code. And I can do that because I can add custom behavior to Strings.

I believe that is simpler and more correct than subclassing String to
get my custom String-like class with custom behavior.

I’ve also grown found of being able to find and slurp in code files and
instantiate classes based on some string. My blogging software allows
one to specify an output format as part of the URL. The code knows how
to break up the path_info string and recognize when, say, there is a
request for rss or xfml or OOo or whatever, It knows that there is a
file named rss.rb or xfml.rb or whatever.rb, with a class named (ready
now?) Rss or Xfml or Whatever. And so on. So I can easily add new
output formats by dropping rendering coding into a running app.

Now, I acquired this taste coding while Java in the late '90s, so this
is not a Ruby thing per se, but I find it easier and more natural in
Ruby. The idea of convention over configuration is quite old (well, as
software ideas go), but dynamic languages strike me as more friendly in
this regard. Far fewer hoops. I don’t get tied down in interfaces and
‘extends’ or ‘implements’ or any of that crap. Code just needs to be
where it is expected and Do The Right Thing when asked.

The URL as [command line|method(arguments)|encoded message] is also not
special to Ruby apps, with a history in PHP/Java/Perl, but again somehow
Ruby makes it easier for me. The line between plain text and program
command is flexible. I like that.

The availability of method_missing allows one to completely decouple the
exposed API (the messages understood by an object) from the
implementation (the methods inside an object), allowing for more robust,
less brittle code.

Plus the ability to do sketch-driven development,
code/run/tweak/test/code while adjusting designs and goals suits my
temperament.

It seems to help me evolve DSLs, and follow the adage, “Build a
language, not an application.”

I’d also like to hear places that people would NOT use ruby due to
it’s open nature, and the reasons for that. I’d like this article to
be more a technical piece on why a little bit of life on the wild side
can be a wise decision, and also why it can be less of a dangerous
endeavor than some might believe when done correctly. I’d like to
avoid zealotry and flamage and my meta-programming-fu can kick your
static ass type things, and instead focus on the nuts and bolts of the
issue.

Good question. I don’t have a real answer. Perhaps for medical
equipment software or something where risk factors are so high that
every and all means to ensure program correctness are required. That
means static typing, unit and functional tests, whatever you can get
your hands on.

Maybe.

James

http://www.ruby-doc.org - Ruby Help & Documentation
http://www.artima.com/rubycs/ - Ruby Code & Style: Writers wanted
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools


#6

Gregory B. wrote:

Prevent what? One can build twisty, loopy, self-modifying code in Java,
too. It’s just painful; maybe that’s part of The Plan.

There is no inherent security from code that is too clever for its own
good.

James

http://www.ruby-doc.org - Ruby Help & Documentation
http://www.artima.com/rubycs/ - Ruby Code & Style: Writers wanted
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools


#7

On 1/8/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

I think it is really just a matter of phrasing the concern in
a different way:

In a statically typed language, the compiler can help to identify
programming errors during the compilation process instead of
at run time.  What tools and/or techniques can be used with Ruby to
identify programming errors before the code is put into production?

Actually, this is not the issue at hand. This really does boil down
to language design in this case. With Ruby’s openness and
meta-programming, even well tested programs can be modified and
redefined dynamically.

This of course, has many benefits, but the bottom line is that Java
was built with a security model to prevent things like this, while
ruby was built to be open from the ground up to facilitate this.

The question is not about security as in exploits necessarily, but as
in unpredictable behavior and the like. In practice, well formed ruby
is every bit as reliable as anything else, and what I’d like to do is
show why


#8

On 1/8/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

If there are vulnerabilities, who are the attackers? I think there are
reasonable issues to discuss in this area but I think the language
choice

This is a good point. It’s hard to make a general statement about
security when you are not sure who you are securing against. I think
that the key issues are secondary libraries modifying other software
and making it unreliable (Such as namespace collisions, unexpected
redefinitions, etc), and the ability to make a set of software behave
in irratic ways by modifying it’s internals via metaprogramming and
the like.

I mean, my general advice when it comes to ruby when asked about
security is that I basically respond, "There is none, but it’s not as
bad as you’d expect. Write proper test suites, code responsibly, and
make sure you nail down those edge cases. Continuous integration is a
must, and idiomatic code with proper style will help make the API
less likely to cause damage (such as the use of ! and other
indicators).

However, to the outsider, this is only an explanation of “how” to
overcome the apparent “flaw”. I’d like to do as good a job I can of
explaining why it isn’t a flaw, when practiced correctly.

I agree that your rephrasing is probably a more sane way to address
the concerns, but we need to consider that there are those who just
assume security is an integral part of their language, and I’d love to
compile a good article about why that’s not always necessary, and in
fact, can be less useful than a more open platform.


#9

On Jan 8, 2006, at 1:14 AM, Gregory B. wrote:

Actually, this is not the issue at hand. This really does boil down
to language design in this case. With Ruby’s openness and
meta-programming, even well tested programs can be modified and
redefined dynamically.

Yes but the testing should identify if the modifications and
redefinitions accomplished their goal, i.e. is the code correct?

It isn’t like the code is randomly modifying code (like certain
languages that allow pointers to any place in memory!). The
modifications are explicit and purposeful and so I think should
be able to be tested as well as any other code.

Gary W.


#10

On 1/8/06, James B. removed_email_address@domain.invalid wrote:

This of course, has many benefits, but the bottom line is that Java
was built with a security model to prevent things like this, while
ruby was built to be open from the ground up to facilitate this.

Prevent what? One can build twisty, loopy, self-modifying code in Java,
too. It’s just painful; maybe that’s part of The Plan.

Though that’s funny, I really think it was part of the plan for Java.
They made no attempt to make doing it convenient or useful (though
that can be said for a lot of Java things), which is part of the way
they can discourage developers from being ‘wild and crazy’

There is no inherent security from code that is too clever for its own
good.

That’s true. We are really addressing the illusion of security. Or
at least a superficial level of security. I think a lot of people are
just scared by how damn convenient and common such practices are in
Ruby, even if their language is capable of doing similar things.


#11

On 2006.01.08 14:01, Gregory B. wrote:

These two.

Which are considered as features by most, but often as vulnerabilities
by outsiders :slight_smile:

You might perhaps point out that professional Smalltalk, Lisp and other
dynamic-language programs have much fewer occurrences of exploits and
ordinary bugs than their static counterparts (this is also relatively
speaking). You can probably find supporting studies if you really want.

I do not have anything in particular to offer for ruby except that above
two are fairly easily and mostly thwarted by proper unit and functional
testing (which one should be doing anyway).

If one were inclined to actually argue a point, one might mention some
of C++'s vulnerabilities: buffer overflows, pointers, memory handling
and so on.

It is, by the way, possible to override C++ methods, access modifiers
and such at runtime as well. These attacks are much more insidious there
as the programmers will not be prepared for them. Google for details.

E


#12

On Jan 8, 2006, at 1:37 AM, Gregory B. wrote:

This is a good point. It’s hard to make a general statement about
security when you are not sure who you are securing against. I think
that the key issues are secondary libraries modifying other software
and making it unreliable (Such as namespace collisions, unexpected
redefinitions, etc),

I can understand the namespace issues. How can I as a programmer
know exactly what is being modified when I add require ‘X’ to my
program? This is an area that of Ruby that has lots of room
for improvement–in documentation of library/class behavior as well
as in possible new language features.

and the ability to make a set of software behave
in irratic ways by modifying it’s internals via metaprogramming and
the like.

I don’t buy this in the sense that I don’t see how this could be
a concern for a dynamic language and not for a static language.
You are still writing code that has to be tested. Whether it is
hard to understand meta-programming or hard to understand data
structures that simulate meta-programming. It is still an issue
of software correctness and I don’t see how static vs. dynamic
changes that issue in any significant way.

Gary W.


#13

On 1/8/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

It isn’t like the code is randomly modifying code (like certain
languages that allow pointers to any place in memory!). The
modifications are explicit and purposeful and so I think should
be able to be tested as well as any other code.

I agree with this and actually am not one who has concerns at all
about ruby’s security or lack thereof. I am a major proponent of the
openness.

However, it’s not random modification but the modification of library
A by library B (or the modification of something in the standard
library by Library C which could effect A, B, and C) that could be a
real concern.

But this is why rubygems allows you to run the unit tests for a
library BEFORE you install it, and if you’ve inspected these unit
tests, optionally adding in the potential edge cases you might expect
to encounter… this would solve a lot of issues.

So that’s my suggestion as one potential ‘solution’ to the proposed
‘problem’.
However, the issue is real and deserved to be addressed. So is it the
consensus of the community that our tests are the core of our safety
net? Or do we have / need extra precautions?

Would it be nice to have a sort of direct access to the standard
library and core units, as well as the units of all installed
libraries, so that these are called on install time or run time or
whenever you need it? Would it be nice to know that library A broke
something in B that library C needed? Or is the practical occurance
of this small enough due to responsible coding and a strong tendency
towards comprehensive testing that we needn’t worry about it.

It’s this i’m not fully sure of, neither in my own convictions nor in
what the community tends to believe. Getting this out in the open
will be a good thing.

I’m a firm believer in Ruby’s open design, I’m a strong proponent of
dynamic code, strongly tested but agile software, and pretty much all
things that are common in Ruby. I’d love to have a definite answer
when the issue of security is brought up though.


#14

On 1/8/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

of software correctness and I don’t see how static vs. dynamic
changes that issue in any significant way.

Honestly, I don’t see this issue either. It’s just a common point the
static people tend to make. Though it is understandable that the fact
that the behavior of your software can change quite drastically once
it’s out of your hands in Ruby moreso than some other languages, this
boils down to good practice and responsibility. If you’re opening up
a class and adding / removing some code, it’s your responsibility to
ensure it doesn’t break things.

If you’re a library maintainer (like many of us are), it’s your job to
identify and test your edge cases. I don’t see this being any
different than anything else.


#15

On 1/7/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

ways
If there are vulnerabilities, who are the attackers? I think there are
reasonable issues to discuss in this area but I think the language
choice
kind of skews the discussion. Why is the situation characterized as a
‘security’ issue? Why are the contents of a library viewed as some sort
of national secret that must be protected from prying eyes/objects?
What is being ‘protected’? Why? From Whom?

From who? Probably stupid programmers who would look up the library’s
implementation and uh, look at it?

I dunno actually.


#16

On 1/8/06, Eero S. removed_email_address@domain.invalid wrote:

If one were inclined to actually argue a point, one might mention some
of C++'s vulnerabilities: buffer overflows, pointers, memory handling
and so on.

This is a good point. In languages like C++, your security can really
go to hell through improper memory management. This is much less
likely to happen in Ruby.


#17

On Sun, 2006-01-08 at 08:44, Gregory B. wrote:

On 1/8/06, Eero S. removed_email_address@domain.invalid wrote:

If one were inclined to actually argue a point, one might mention some
of C++'s vulnerabilities: buffer overflows, pointers, memory handling
and so on.

This is a good point. In languages like C++, your security can really
go to hell through improper memory management. This is much less
likely to happen in Ruby.

It is a good point when comparing Ruby and C++, but it is unlikely to
impress Java programmers.

I believe it will be a better strategy to argue the strengths of Ruby,
rather than the weaknesses of other languages. For example, on the Java
side, there are frameworks who aim to make software designs more loosely
coupled, for example IoC frameworks, AOP extensions, etc. These
frameworks emulate a dynamic approach to software development. Some of
these, like Spring, are very good. Nevertheless, learning those
frameworks requires a considerable investment in time and effort. The
proliferation of different frameworks makes it hard to get by knowing
just one or two. (One thing that maybe shouldn’t be brought up is that
often corporate policies, rather than technical considerations, dictate
which framework gets used. For example, many companies use EJB and
various EJB frameworks even though it is not appropriate for the
development they do.)

Ruby, as we know, has many AOP features built in. The dynamic typing
removes the need for complex interface based designs. More important,
the Ruby features are easy to learn, easy to use, and because they are
built-in, they don’t run the risk of being prohibited by corporate
policies.

/Henrik

http://www.henrikmartensson.org/ - Reflections on software development


#18

On Jan 8, 2006, at 2:40 AM, Gregory B. wrote:

Though it is understandable that the fact
that the behavior of your software can change quite drastically once
it’s out of your hands in Ruby moreso than some other languages, this
boils down to good practice and responsibility. If you’re opening up
a class and adding / removing some code, it’s your responsibility to
ensure it doesn’t break things.

One of the things I really liked about Bertrand Meyer’s Object Oriented
Software Engineering
was his ‘programming by contract’ point of view.
I’ve found that concept to be useful in almost any program I’ve written
after reading that book regardless of the language I’ve used.

One of the most useful ideas is the notion that if client code
refuses to adhere to the pre-conditions then all bets are off and the
library code has no responsibility to ensure correct behavior.

To a certain extent I think the concerns that static people have about
Ruby seem to be concerns about what happens if they violate the contract
by not ensuring the pre-conditions at run-time. The response
should be:
Why is that my problem? The water is under the bridge at that point.
Game over.

More important is answering:
How can I discover the pre-conditions associated with Ruby code
How can I avoid writing code that breaks the pre-conditions?
How can I determine the post-conditions?

So if there are valid criticism of the Ruby approach I think it could
be said
that often times the pre-conditions (and post-conditions) are not
documented
very well (especially with regard to meta-programming).

Documentation can help but I think there is room for language
features in this
area also (e.g. structured annotations). Namespaces will also
help to localize effects and provide a framework for describing those
effects.

Quick Question: What core classes are modified by ActiveRecord or Og?
Quick Answer: Lots but good luck finding a concise description.

Gary W.


#19

On Jan 8, 2006, at 12:14 AM, Gregory B. wrote:

Actually, this is not the issue at hand. This really does boil down
to language design in this case. With Ruby’s openness and
meta-programming, even well tested programs can be modified and
redefined dynamically.

This of course, has many benefits, but the bottom line is that Java
was built with a security model to prevent things like this, while
ruby was built to be open from the ground up to facilitate this.

Sentences like the above always read to me as: “Java was designed to
protect the programmer from doing programmer things.” Always sounds
funny to me.

James Edward G. II


#20

On Jan 8, 2006, at 1:37 AM, Gregory B. wrote:

However, it’s not random modification but the modification of library
A by library B (or the modification of something in the standard
library by Library C which could effect A, B, and C) that could be a
real concern.

It’s a blessing and a curse, like most things. Infinite power,
infinite responsibility, and all that rot.

It came up here recently, in fact. Until Ruby 1.8.3, Logger didn’t
allow users to specify the output format. Because of that, some
people hacked into the class and replaced the proper guts to make it
work. It was nice to be able to do this, but of course the Logger
API was updated and some projects, including Rails, broke. Rails has
since been patched to work around this, of course.

Here’s a thought though… Let’s forget for a moment how it
happened and just look at what happened. Short story: An upgrade
introduced an incompatibility bug with some dependent software. That
happens with tons of projects in every language and is only even of
note here because of how the incompatibility was introduced.

James Edward G. II