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

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

} 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

···

On Sun, Jan 08, 2006 at 11:28:17AM +0900, Gregory Brown wrote:

Gregory Brown 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.

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
Ruby Code & Style - 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

These two.

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

···

On 1/7/06, Gregory Seidman <gsslist+ruby@anthropohedron.net> wrote:

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

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

Gregory Seidman wrote:
...

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

Also I consider myself a bad programmer I want to share my opinion on this:

4) is about social security. the private and protected stuff I know of java and all those things enable the tool language to be used by management to enforce devision of work.

the management and team leaders can create a work model and thus can set define who is responsible for what within the tools of work - the programming language.

this opens a discussion I cannot join cause I am not working in larger companies with high distances between management and workers (=programmers).

I think what java (and possible c++, but i don't know) offers can be archived in another way - but well, not by using ruby itself. Further I think it is the wrong place to implement such "security" features in work processes cause it can HINDER and reduce efficiency, creativity, output. I think working with a modular application model in mind may solve this. If you got commercial software written by software giants (this is where, i think, these "security" models, added onto the object-orientation-model cause it was just possible, apply) you can instead of taking your time to develop and implement a language-side "security"-model take your time and specify interfaces between parts, modules, of your software. different teams will work on different modules then which will run in different VMs communicating via inter process messaging or via databases for example.

maybe my view is very narrow, but well, maybe it helps.

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 Wright

···

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

On 1/7/06, Gregory Seidman <gsslist+ruby@anthropohedron.net> wrote:

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

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

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

···

On 2006.01.08 14:01, Gregory Brown wrote:

On 1/7/06, Gregory Seidman <gsslist+ruby@anthropohedron.net> wrote:

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

> 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

These two.

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

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*

···

On 1/8/06, gwtmp01@mac.com <gwtmp01@mac.com> 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?

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.

···

On 1/8/06, gwtmp01@mac.com <gwtmp01@mac.com> 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. In languages like C++, your security can really
go to hell through improper memory management. This is much less
likely to happen in Ruby.

···

On 1/8/06, Eero Saynatkari <ruby-ml@magical-cat.org> 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.

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

I dunno actually.

···

On 1/7/06, gwtmp01@mac.com <gwtmp01@mac.com> wrote:

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

> On 1/7/06, Gregory Seidman <gsslist+ruby@anthropohedron.net> wrote:
>
>> I'm not clear on what openness we're talking about. Do you mean
>> one or more
>> of the following:
>
>> 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
>
> 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?

Gregory Brown wrote:

···

On 1/8/06, gwtmp01@mac.com <gwtmp01@mac.com> 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.

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
Ruby Code & Style - 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

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 Wright

···

On Jan 8, 2006, at 1:14 AM, Gregory Brown 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 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 Wright

···

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

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

···

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

On 1/8/06, Eero Saynatkari <ruby-ml@magical-cat.org> 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.

--

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

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 Gray II

···

On Jan 8, 2006, at 12:14 AM, Gregory Brown 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.

Don't let the java guys off so easily, either.

- In Java it's possible to add classes to a package unless that package is sealed.
- When inner classes are compiled in Java they are converted to ordinary classes. Any private fields of the containing class are converted to public fields.
- Cloning an object in Java bypasses its constructor.
- Non-clonable classes can be extended and the child can implement cloneable.
- Java objects can be serialized exposing state, including private fields.
- Non-serializable objects can be sub-classed just like non-cloneable objects.
- Serialized objects can be deserialized, bypassing the constructor.
- Static fields are essentially globals, discoverable and settable by anyone.

All of these can be addressed with good, but tedious, coding practices, but still when trying to secure Java code from another programmer, you have your work cut out for you. This is especially tricky in Java "frameworks" and development tools that routinely serialize/deserialize objects and use reflection to create objects at runtime.

Pete

···

On Jan 8, 2006, at 2:44 AM, Gregory Brown wrote:

On 1/8/06, Eero Saynatkari <ruby-ml@magical-cat.org> 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.

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).

There's documentation for how to do good APIs here:
http://rpa-base.rubyforge.org/wiki/wiki.cgi?GoodAPIDesign

I hope that's useful; feel free to edit and add/change/add discussion.

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.

Let's look at it as a cost/benefit analysis. The cost of declaring
variables and types end up as roughly half the code size. That's
twice the amount to write, and, more importantly twice the amount to
read, twice the amount of places to change when refactoring, etc. It
also means that there's a lot of things we can't do, because we are
"protected" from it.

At this cost, the type and variable declarations had better give us a
lot. In practice, I find that they give me very little, bug wise:
Maybe 5% of my simplest bugs are detected by them. The advantages I
get are in the speed of the compiled code, and as documentation.
However, these benefits are too small to be worthwhile for the size
projects I presently do (one and two person projects).

I've implemented a system for doing run time checks of type
declarations, it's available from RPA (as types) and from
Index of /~eivind/ruby/types/ This allows very
flexible type checks for Ruby programs, adding whatever amount of type
discipline you want. In practice, I found this to just get in the way
- it detected very few bugs, and added more stuff to change when I did
refactoring.

Eivind.

···

On 1/8/06, Gregory Brown <gregory.t.brown@gmail.com> wrote:

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.

There seems to be some sort of logical fallacy at play here, but I
can't sniff out what it is. Arguing from authority? Begging the
question?

You seem to be half-asking for good answers to "Is Ruby's openness a
flaw?" and at the same time looking for good answers to the argument
"Why Ruby's openness isn't a flaw."

I would argue that it IS a flaw...for some people, use cases, or
programming styles. At the same time, I would then argue that it's also
a huge feature...for some people, use cases, or programming styles.

Don't try to convince people that they're wrong for wanting a compiler
to catch certain typos for them without writing use cases. Don't tell
them "if you include libraries A and B in your application, and B
modifies A in a way that neither's documentation covers...that's a
feature! You should embrace it, not hate it!"

Instead, convince them that use cases are more secure than the false
sense of security syntax- and static-checking provide. Acknowledge that
there ARE some downsides to the openness, but that they are outweighed
by the freedom provided.

This thread is not about a language war, but touches on issues at the
fringes of one. Remember that no one language is the Right language for
all cases. There may be cases where the openness of Ruby makes it the
wrong choice.

To convince people that Ruby is Right for cases where they are clinging
to a few misconceptions:

1) Identify clear problems, or perceived problems.
2) One by one, lay out:
2a) What the problem is in Ruby.
2b) Why it isn't (or is) a problem in the 'standard' language of
choice.
2c) How you can remove or mitigate the problem in Ruby using additional
features or changed coding styles. If you can't, say so.
2d) What benefits are made possible by the features of Ruby that cause
the problem.

The combination of 2c and 2d should give the listener a good idea of
cost/benefits. 2b may give the user an 'ah-ha' moment, realizing they
have misconceptions, or have been relying on a false sense of security.

> 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.

···

On 1/8/06, James Britt <james_b@neurogami.com> wrote: