LoginSignup
33
21

More than 5 years have passed since last update.

public_sendを信用しちゃいけないよ、というお話

Last updated at Posted at 2014-01-03

Rubyでは、privateなメソッドは普通に呼んでもエラーになります。

class Foo
  private
  def bar
    puts "Don't call me!"
  end
end

Foo.new.bar
#=> NoMethodError: private method `bar' called for #<Foo:0x007fe1232991c0>

一方、Rubyにはsendというメソッドがあって動的なディスパッチが可能となっていますが、これはprivateなメソッドも呼べてしまいます。

Foo.new.send :bar
#=> Don't call me!

そこで、publicなメソッドのみが呼べるpublic_sendというものが用意されています。

Foo.new.public_send :bar
#=> NoMethodError: private method `bar' called for #<Foo:0x007fe123238aa0>

しかし、public_sendにはこんな抜け道が……

Foo.new.public_send :instance_eval, 'bar'
#=> Don't call me!

そう、instance_evalを使い、引数として呼びたいメソッドの名前を「文字列で」渡せばprivateなメソッドも呼べてしまうのです(シンボルでは動作しません)。
この辺はRubyの自由度の高さですが、それが裏目に出る場合もあります。
例えばRailsで外部からの入力を直接、動的にディスパッチする必要がある場合、public_sendを使うだけでは危険です。おそらく呼びたいメソッドの種類はそう多くないので、ホワイトリスト形式でそれらをリストアップし、残りはsendすべきではないでしょう(そもそもそんな危ない橋を渡るな、という話ですが……)。

whitelist = %w(foo bar buzz)
if whitelist.include? params[:method_name]
  obj.public_send params[:method_name]
else
  raise "Not in whitelist"
end

以上、Rubyの世界ではすべてがpublicなんだ!というお話でした。

追記:
参考:http://www.phenoelit.org/stuff/hitb2013ams/#/

33
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
21