「Ruby」の正規表現エンジン「Onigmo」にリモートコード実行の問題、セキュリティ更新を実施 - 窓の杜 のように Ruby 本体としては、そこに任意のユーザー入力を渡しているのはプログラム側の問題で Ruby 本体の脆弱性としては扱わない、 という方針になっているメソッドがあるので、思い付く限り列挙してみます。

eval 系

当然 eval は任意のコードを実行するためのものなので、外部入力を含む任意のユーザー入力を渡すようなものではありません。 class_eval, module_eval, instance_eval などの他の類似メソッドも同様です。

system 系

system や Kernel#` や spawn などの外部コマンドを実行するメソッドに任意の入力を渡すとシェルコマンドインジェクションが可能になります。

Ruby 本体の機能ではありませんが、SQL も同様に直接外部入力を渡すと SQL インジェクションが可能になります。

send 系

send や public_send なども任意のメソッドが呼び出せると引数の数に制限があっても、組み合わせ次第で任意コードの実行に繋がることがあります。

Marshal 系

Marshal.load

色々な方法で任意コードを実行する方法が発見され、仕組み的に根本的な解決は難しいとされています。 内部で Marshal を使う dRuby をそのままインターネット上に公開してはいけない理由でもあります。

YAML.unsafe_load

YAML で昔の load が unsafe_load になり、名前からわかる通り信頼できない入力を渡すには危険なので、新しい load か safe_load を使います。

JSON.load

Security/JSONLoad によると、 JSON は JSON.parse か JSON.unsafe_load を使うか、create_addition: を明示して JSON.load を使うと良さそうです。

Regexp.new 系

Regexp.new や Regexp.complie や /#{input}/ などで正規表現の一部として任意のユーザー入力を使うのは、 普通のプログラムではすべきではなく、 プログラム側の問題として Ruby 本体では脆弱性としては扱わないとしているようです。

IO 系

IO.read 系

Security/IoMethods のように | で始まる文字列で任意コマンドが実行可能になるので危険です。

File.read 系

任意コマンド実行ではないですが、 | が使えない File.read 系でも任意のファイルが読めて危険です。

open-uri の open

関連して open-uri の open を使っていた場合の書き換え先としては URI.open になっていますが、 明確に URI だけ受け付けたい場合は URI#open に書き換える方が好みです。 (どこまで安全かは確認していません。)

%  ruby -r open-uri -e 'URI.open("/etc/hosts"){}'
%  ruby -r open-uri -e 'URI("/etc/hosts").open{}'
-e:1:in '<main>': private method 'open' called for an instance of URI::Generic (NoMethodError)

番外編: ホスト名

任意の URL を入力して何か情報を返すサービスがあって、制限をしていないと、 たとえば自宅サーバーで動かして 192.168.0.1 の URL でルーターの情報を返してしまったり、 EC2 で動かしていて 169.254.169.254 の URL で EC2 のメタデータを返してしまったりする可能性が考えられます。

IP アドレス直接だけ塞いでも 169-254-169-254.nip.io などの任意の FQDN で対象の IP アドレスを設定すればいいので、 普通の DNS 問い合わせを許可している限りホスト名レベルでの完全な対策は不可能です。

AI 時代で攻撃側はこういう対象は簡単に発見できそうなのに、防御側は何年も前から気にしていても未だに解決できていないことがあります。

隠しておくことによる防御が有効な時代ではなくなっているようなので、隠しておいた方が安全そうだと思っていた情報のうち、公開可能なものは機会をみて公開していこうと思っています。

まとめ

Ruby には信頼できない任意のユーザー入力を想定していないメソッドがあります。 昔は (たぶん Perl 由来の) taint フラグや (Ruby 独自の) untrust フラグで対処しようとしていましたが、不十分だったのでフラグ自体が消えました。 安全でないユーザー入力を渡しているかどうかは、ちゃんと自分で管理する必要があるので、渡してしまわないように気をつけましょう。

Disqus Comments

Kazuhiro NISHIYAMA

Ruby のコミッターとかやってます。 フルスタックエンジニア(って何?)かもしれません。 About znzに主なアカウントをまとめました。

znz znz


Published