動かざることバグの如し

近づきたいよ 君の理想に

RubyでFile.read()を使うときは注意が必要だった(過去形)

まとめ

  • Ruby 2.6以前ではFile.read()の引数に|から始める文字列を渡すと、それがそのままコマンドとして実行されてしまう
    • ので、WEBアプリケーションで第三者から受け取ったパラメータをFile.read()に渡すのはセキュリティ的に危険
  • Ruby 2.6.0で修正されたので今後は大丈夫

どういうことか

ちょうどRubyKaigi2019の講演聞いている最中の話。実際に挙動を見てみる

~ $ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18]
~ $irb
irb(main):001:0> File.read("|echo 'hello world'")
(irb):1: warning: IO.read called on File to invoke external command
=> "hello world\n"

確かに、コマンドが実行できる。。。!

この挙動は別にバグでもなく仕様で、そもそもFileクラスはIOクラスを継承している。

で、IO.read()の公式ドキュメントを見るとたしかに

Kernel.#open と同様 path の先頭が "|" ならば、"|" に続くコマンドの出力を読み取ります。

となっている。つまり、IO.read()の仕様がそのままFile.read()にも来ているだけである。

IO.read()でコマンド実行できるのセキュリティ的にどうなん?ってIssueが上がって、けどそれはアプリケーションレベルでの問題で、Rubyの問題ではないって話になり、けどFile.read()でまさかコマンドが実行できるのはちょっと(期待される挙動として)おかしくないかって別Issueが上がって、そっちは採択された模様。

なので前述のとおり、Rubyの2.6以降では実行できなくなっている

~ $ruby -v
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin16]
~ $irb
irb(main):001:0> File.read("|echo 'hello world'")
Traceback (most recent call last):
        5: from /Users/thr3a/.rbenv/versions/2.6.1/bin/irb:23:in `<main>'
        4: from /Users/thr3a/.rbenv/versions/2.6.1/bin/irb:23:in `load'
        3: from /Users/thr3a/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        2: from (irb):1
        1: from (irb):1:in `read'
Errno::ENOENT (No such file or directory @ rb_sysopen - |echo 'hello world')

こうやってどんどんRubyはよくなっていくんだなぁ(こなみ いやほんとありがとうございます