ブランクスレート(BasicObject)のインスタンスでinstance_evalするのはセキュリティ対策になりうるか?
きっかけ
"x: 100, y: 200, z: 300" のような文字列を
{x: 100, y: 200, z: 300}のようなハッシュに変換する方法
:パターン2 eval使う
超簡単(ruby怖っ)
def to_hash(str) eval("{#{str}}") end
を読んで、ふと
「eval
族を使うのは怖いけど、 BasicObject#instance_eval
であれば str
に何らかのメソッドを使われたとしても NoMethodError
になるのではなかろうか。リテラルは使えるし、これって eval
でのセキュリティ対策に使えるかも?」
と思いました。
Rubyist の共通見解として「eval
族と method_missing
は最終兵器」というのがあるかと思いますが、 BasicObject#instance_eval
を使うと少し気楽に eval
できるかも、と考えました(速度などは考慮してません)。
具体的にみてみる
上記例を実行
$ pry
[1] pry(main)> str = "x: 100, y: 200, z: 300"
=> "x: 100, y: 200, z: 300"
[2] pry(main)> BasicObject.new.instance_eval("{#{str}}")
=> {:x=>100, :y=>200, :z=>300}
問題なくハッシュを生成できました。
インジェクションしてみる1:メソッド
[3] pry(main)> str = "};puts 'hello!';{"
=> "};puts 'hello!';{"
[4] pry(main)> BasicObject.new.instance_eval("{#{str}}")
NoMethodError: undefined method `puts' for #<BasicObject:0x007fa980ca39c0>
from (pry):4:in `instance_eval'
無事に NoMethodError
になりました。
インジェクションしてみる2:コマンド出力
[5] pry(main)> str = "};`echo hello`;{"
=> "};`echo hello`;{"
[6] pry(main)> `echo hello`
=> "hello\n"
[7] pry(main)> BasicObject.new.instance_eval("{#{str}}")
NoMethodError: undefined method ``' for #<BasicObject:0x007fa9814fe0e8>
from (pry):7:in `instance_eval'
str = "};%x[echo hello];{"
でも同じく NoMethodError
でした。
定数設定
[8] pry(main)> ARY
NameError: uninitialized constant ARY
from (pry):8:in `__pry__'
[9] pry(main)> str = '};::ARY = %w[a b c];{'
=> "};::ARY = %w[a b c];{"
[10] pry(main)> BasicObject.new.instance_eval("{#{str}}")
=> {}
[11] pry(main)> ARY
=> ["a", "b", "c"]
設定されてしまいました。
グローバル変数設定
[12] pry(main)> $ary
=> nil
[13] pry(main)> str = '};$ary = %w[a b c];{'
=> "};$ary = %w[a b c];{"
[14] pry(main)> BasicObject.new.instance_eval("{#{str}}")
=> {}
[15] pry(main)> $ary
=> ["a", "b", "c"]
グローバル変数も設定されてしまいました。
質問
BasicObject#instance_eval
だと定数とグローバル変数が犯されうることがわかりましたが、そのほかに危険性はあるでしょうか。具体的に攻撃文字列を挙げていただければ幸いです。
また、定数とグローバル変数が犯されると、どのような危険性が考えられるでしょうか。
以上2点について、よろしくお願いいたします。