From: Shugo Maeda Date: 2009-07-19T01:05:29+09:00 Subject: [ruby-dev:38821] セキュリティモデルのドキュメント 前田です。 田中さんに会うたびに書けと言われるので、$SAFEまわりのドキュメントの叩き台を 作ってみました。 禁止される操作の一覧はリファレンスマニュアルから取って来ましたので、実装とは 乖離があるかもしれません。 とりあえずは、叩き台ということで…。 = Rubyセキュリティモデル Rubyのセキュリティモデルは、以下の2つの異なる目的のために提供されている。 : 外部の資源の保護 プログラム外部からの入力データによって、プログラム外部の資源に対す る、プログラマの意図に反した操作が行われることを防ぐため。たとえば、 Webアプリケーションに対して不正なクエリ文字列が与えられた場合に、そ の文字列を元にファイル操作や外部コマンドの実行などの操作が行われる ような危険性から、プログラム外部の資源を保護することを想定している。 : 信頼されていないコードの実行 信頼されていないコードを、他のデータから隔離された環境(サンドボック ス)内で安全に実行するため。 ただし、現在のRubyの実装では、この機能は完全ではない。詳細について は「セーフレベル4の問題点」を参照。 == オブジェクトのフラグ Rubyのオブジェクトは、taintedとuntrustedと呼ばれる2つのフラグを持つ。 === tainted taintedは、オブジェクトが汚染されているという状態を表すフラグであり、外 部の資源の保護のために利用される。taintedフラグが設定されたオブジェクト を「汚染されたオブジェクト」、設定されていないオブジェクトを「汚染され ていない」と呼ぶ。 このフラグは以下のような場合に設定される。 * オブジェクトが、外部からの入力(IO・コマンドライン引数・環境変数など) を元に生成された場合。環境変数PATHだけは例外で、値に危険なパスを含む 場合のみ汚染される。ここでは危険なパスとは誰でも変更・書き込みが可能 なパスをいう。ルートディレクトリから階層が順番にチェックされ、一箇所 でも誰でも変更可能な個所があればそのパスは危険とみなされる。 * オブジェクトが、セーフレベル3以上で生成された場合。 * オブジェクトが、他の汚染されたオブジェクトを元に生成された場合。 * Object#taintメソッドによって明示的に設定された場合。 また、Object#untaintメソッドによってフラグを除去することができる。 taintedフラグが設定されているかどうかは、Object#tainted?メソッドによっ て検査することができる。Object#tainted?は、フラグが設定されている場合に trueを、設定されていない場合にfalseを返す。 === untrusted untrustedは、オブジェクトが信頼されていないコードによって生成されたという状 態を表すフラグであり、信頼されていないコードをサンドボックス内で安全に実行 するために利用される。untrustedフラグが設定されたオブジェクトを「信頼さ れていないオブジェクト」、設定されていないオブジェクトを「信頼されたオ ブジェクト」と呼ぶ。 このフラグは以下のような場合に設定される。 * オブジェクトが、セーフレベル3以上で生成された場合 * オブジェクトが、他の信頼されていないオブジェクトを元に生成された場合 * Object#untrustメソッドによって明示的にフラグを設定された場合 また、Object#trustメソッドによってフラグを除去することができる。 untrustedフラグが設定されているかどうかは、Object#untrusted?メソッドに よって検査することができる。Object#untrusted?は、フラグが設定されている 場合にtrueを、設定されていない場合にfalseを返す。 == セーフレベル Rubyのセキュリティチェック機能は、セーフレベルによって制御される。セー フレベルはスレッドローカル変数$SAFEで設定する。スレッドが作成された場合 は、親スレッドの$SAFEの値を引き継ぐ。 デフォルトでは、セーフレベルは0である。セーフレベルは、Rubyの起動オプショ ン-Tで設定することができる。 $SAFE の値を現在の値より小さく変更する事はできない。 $ ruby -e '$SAFE = 1; $SAFE = 0' -e:1:in `
': tried to downgrade safe level from 1 to 0 (SecurityError) Procオブジェクトのブロック内で$SAFEの値が設定された場合は、Procオブジェ クトの実行終了後に$SAFEの値は実行前の状態に戻される。 $ ruby -e 'lambda { $SAFE = 1 }.call; p $SAFE' 0 各セーフレベルでは、以下のようなチェックが行われ、禁止された操作が実行 された場合にはSecurityError例外が発生する。 === レベル0 デフォルトのセーフレベル。 ==== 禁止される操作 レベル0では、禁止される操作は何もない。 === レベル1 レベル1では、プログラム外部からの入力データによって、プログラム外部の資 源に対する、プログラマの意図に反した操作が行われることを防ぐため。 ==== 禁止される操作 * 汚染された文字列を引数とした以下の操作 * Dir, IO, File、FileTestのクラスメソッド、メソッド $ ruby -e '$SAFE = 1; open(ARGV[0])' hoge -e:1:in `initialize': Insecure operation - initialize (SecurityError) from -e:1:in `open' from -e:1 * ファイルテスト演算子の使用、ファイルの更新時刻比較 * 外部コマンド実行(system, exec, ``) * eval (レベル4の説明も参照) * トップレベルへのload(第二引数を指定してラップすれば実行可能) * require * trap * 外部の資源に対するその他の操作 * 外部コマンド実行(環境変数PATHに危険なパスを含んでいる場合のみ) === レベル2 ==== 禁止される操作 レベル1で禁止される操作に加えて、以下の操作が禁止される。 * Dir.chdir Dir.chroot Dir.mkdir Dir.rmdir * File.chown File.chmod File.umask File.truncate File#lstat File#chmod File#chown File#delete File#unlink File#truncate File#flock およびFileTestモジュールのメソッド * IO#ioctl, IO#fcntl * Process.fork Process.setpgid Process.setsid Process.setpriority Process.egid= Process.kill * 危険なパスからのload * 汚染された文字列を引数にしてのload(ラップされていても) * syscall * exit! * trap === レベル3 レベル3は、信頼されていないコードを実行するための環境を作成するために利 用される。レベル3で生成されるオブジェクトには、taintedおよびuntrustedフ ラグが設定される。 ==== 禁止される操作 レベル2で禁止される操作に加えて、以下の操作が禁止される。 * Object#untaint * Object#trust === レベル4 レベル4は、信頼されていないコードを実行するために利用される。ただし、こ の機能は完全ではない。詳細については、「セーフレベル4の問題点」を参照。 このレベルでは、危険な操作がすべて禁止されているため、レベル3では禁止さ れている「汚染された文字列のeval」が許可されている。 ==== 禁止される操作 レベル3で禁止される操作(evalは除く)に加えて、以下の操作が禁止される。 * Object#taint * Object#untrust * トップレベルの定義の変更(autoload, load, include) * 既存のメソッドの再定義 * Objectクラスの定義の変更 * 信頼されたクラスやモジュールの定義の変更 およびクラス変数の変更 * 信頼されたオブジェクトの状態の変更 * グローバル変数の変更 * 信頼されたIOやFileを使用する処理 * IOへの出力 * プログラムの終了(exit, abort) (なおout of memoryでもfatalにならない) * 他のスレッドに影響が出るThreadクラスの操作 および他のスレッドのThread#[] * ObjectSpace._id2ref * ObjectSpace.each_object * 環境変数の変更 * srand * 外部の資源に対するその他の操作 == untaintについての指針 === アプリケーションにおけるuntaint セーフレベル1以上で、汚染されたオブジェクトによって外部の資源の操作を行 う必要がある場合には、Object#untaintによってtaintedフラグを除去する必要 がある。 ただし、untaintを行う前には、そのオブジェクトを利用して当該操作を行って も問題ないことのチェックをオブジェクトに対して行うべきである。 === ライブラリにおけるuntaint メソッドの引数などでユーザから与えられたデータが汚染されたオブジェクト でない場合は、ユーザが指定した操作の実行を意図していると判断し、処理の 実行に必要な内部データのuntaintは、ライブラリ側で行うことが望ましい。 ライブラリ内部で生成されるオブジェクトについては、ライブラリのユーザ側 からuntaintすることができないため、上記のようなuntaintを行わないと、そ のライブラリをセーフレベル1以上では利用することができないためである。 == 拡張ライブラリの作成指針 拡張ライブラリの作成の際には、以下の点に注意する必要がある。 === セーフレベル4における操作の禁止 セーフレベル4では、外部の資源に対する操作を禁止すべきである。操作の禁止 には、rb_secure()を利用する。 static VALUE readline_readline(int argc, VALUE *argv, VALUE self) { ... rb_secure(4); === 外部からの入力のtaint データベースからのデータの読み込み時など、外部からの入力を元にオブジェ クトを生成する場合は、生成されたオブジェクトをOBJ_TAINT()によりtaintす べきである。 文字列の生成時には、直接OBJ_TAINT()を呼び出す代りに、 rb_tainted_str_new()やrb_tainted_str_new_cstr()を利用することもできる。 == セーフレベル4の問題点 現在のセーフレベル4の実装は次のような問題を持っており、完全ではない。 * 無限ループやスタックオーバーフローの検出などを行わないため、DoS状態が 引き起こされる可能性がある。 * 拡張ライブラリなどについては、セーフレベルのチェックを行っていないも のがあるため、外部の資源に対する操作を実行される可能性がある。 -- Shugo Maeda