Question

w = Widget.new # Create a Widget
w.send :utility_method # Invoke private method!
w.instance_eval { utility_method } # Another way to invoke it
w.instance_eval { @x } # Read instance variable of w

Looking at the example above which relates to the Widget class (below), the send and instance_eval methods violate all of the protections provided by private and protected visibility. If so, why bother with private and protected access in Ruby at all since there is no guarantee that your definitions will be honored?

class Widget
  def x # Accessor method for @x
   @x
  end
  protected :x # Make it protected
  def utility_method # Define a method
   nil
  end
  private :utility_method # And make it private
end
Was it helpful?

Solution

If you really want to protect instances of Widget, you can do this (and a bunch of other stuff; the code here is not a complete security solution, merely indicative):

class Widget

  def some_public_method
    ...
  end

  private

  def utility_method
    ...
  end

  def send(method, *args, &block)
    raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
  end

  def instance_eval(&block)
    raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
  end

  class <<self
    private
    def class_eval(&block)
      raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
    end
  end
end

Widget.freeze

OTHER TIPS

ruby believes in giving you the power to do what you want. it just doesn't make it easy to inadvertently shoot your foot off - if you want to subvert the private declarations, you have to use syntax that makes it clear you are doing so. note that the person finally deciding what the code should or shouldn't do is the person using a library, not the person writing it.

I cannot comment, because of low rep :/.

Redefining send is no use, because send is just the common name for __send__ (thats underscore,underscore,"send",underscore,underscore), which is the method actually implementing message sending. Redefining any __method__ is not recommended. Additionally, the other person can also reopen the class and revert the definition:

class Widget
  def send(method, *args, &block)
    super
  end
  #and so on
end

In Ruby 1.9, the behaviour is slightly different: #send actually honors visibility, __send__ doesn't.

private in Ruby has more of a declarative purpose: methods declared as private are an implementation detail and not an API detail. You are not allowed to send a message from the outside by accident. But anyone can still forcefully circumvent that restriction, if they see fit - on their own account.

At the very least you express what the public API of the Widget class is.

The take home message is: don't bother.

Ruby, like Python, absolutely sucks at sandboxing. If you try to lock something down, chances are there will always be some way to get around it. The multitude of ways to get a private attribute in Ruby proves my point.

Why? Because they are designed to be that way. Both languages are designed so that they can be poked around with at runtime – it's what gives them their power. By sealing up your class, you're depriving others of the power that Ruby's metaprogramming provides.

Java has reflection. C++ has pointers. Even Haskell has unsafePerformIO. If you want to protect your program, you will need to protect it on the operating system level, not using the language.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top