まとめ
- Ruby 2.6以前では
File.read()
の引数に|
から始める文字列を渡すと、それがそのままコマンドとして実行されてしまう- ので、WEBアプリケーションで第三者から受け取ったパラメータを
File.read()
に渡すのはセキュリティ的に危険
- ので、WEBアプリケーションで第三者から受け取ったパラメータを
- 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はよくなっていくんだなぁ(こなみ いやほんとありがとうございます