「オブジェクトを汚染する」という厨二感あふれるワードに惹かれて、
taintに触れてみました。
畏れ多き、
rubyのセキュリティ機構のご報告になります!
タイトルの3分は関係ありません。
rubyのセキュリティモデル
Q:「rubyのセキュリティって何?」
A:
RubyにはCGI等のプログラミングを安全に行うことを助ける為に、
セキュリティ 機構が備わっています。
Rubyのセキュリティモデルは
「オブジェクトの汚染」と「セーフレベル」という仕組みによってなりたっています。
引用:Ruby2.4.0リファレンスマニュアル>セキュリティモデル
セーフレベルと禁止される操作
レベル0とレベル1があります。
2,3,4もあったみたいですが廃止されました。
デフォルトではレベル0にされていて、
レベル0では禁止される操作はないですが、
レベル1で以下の操作が禁止されます。
- 汚染された文字列を引数とした以下の操作
- ファイルテスト演算子の使用、ファイルの更新時刻比較
- 外部コマンド実行 (Kernel.#system, Kernel.#exec, Kernel.#`, Kernel.#spawn など)
- Kernel.#eval
- トップレベルへの Kernel.#load (第二引数を指定してラップすれば実行可能)
- Kernel.#require
- Kernel.#trap
taintを使ってみる
セーフレベルを変更(①)し、
オブジェクトを汚染(②)し、
操作が禁止されていることを確認(③)してみましょう。
##①セーフレベルを変更
セーフレベルはスレッドローカル変数$SAFE
で設定できます。
p $SAFE #デフォルトは0
# => 0
$SAFE = 1 # これで変更できる!
p $SAFE
# => 1
$SAFE = 0 # 現在のレベル以下には設定できない
# => SecurityError: tried to downgrade safe level from 1 to 0
# from (irb):29
# from /usr/bin/irb:12:in `<main>'
もし、部分的にセーフレベルをあげたい時はThreadを使いましょう。
各スレッドは作られた時点での親スレッドの$SAFEの値を引き継ぎます。
比較対象としてhogeを定義しました。
$SAFE = 1
hoge = 1
th = Thread.new{
p $SAFE #=> 1
$SAFE = 2
p hoge #=> 1
hoge = 2
}
th.join
p $SAFE #=> 1
p hoge #=> 2
②オブジェクトを汚染
以下のコードでは、
code2は汚染されていますが、
セーフレベルが0なので操作が禁止されていません。
p $SAFE #=> 0
code1 = "puts 'This is untainted'"
eval(code1) #=>This is untainted
code2 = "puts 'This is tainted'"
code2.taint # code2を汚染する
eval(code2) #=> This is tainted
# code2の汚染状態を確認
code2.tainted? #=> true
③操作が禁止されていることを確認
②で使ったコードをセーフレベルをあげるとeval(code2)
で、
SecurityError
が発生し、
禁止されていることが確認できます。
$SAFE = 1
p $SAFE # => 1
code1 = "puts 'This is untainted'"
eval(code1) #=>This is untainted
code2 = "puts 'This is tainted'"
code2.taint # code2を汚染する
eval(code2)
#=> SecurityError: Insecure operation - irb_binding
# from (irb):33:in `eval'
# from (irb):33
# from /usr/bin/irb:12:in `<main>'
# code2の汚染状態を確認
code2.tainted? #=> true
ちなみに、untaint
でオブジェクトの汚染を除くこともできます。
code2 = "puts 'hogehoge'"
code2.taint # code2を汚染する
eval(code2) #=> SecurityError ...
code2.tainted? #=> true
code2.untaint # code2の汚染を除く
eval(code2) #=> hogehoge
code2.tainted? #=> false
#まとめ
セーフレベルまとめ
- プログラム開始時の$SAFEの値は0
- 各スレッドは作られた時点での親スレッドの$SAFEの値を引き継ぐ
- $SAFE の値を現在の値より小さく変更する事はできない
taintまとめ
taint : オブジェクトを汚染する。
tainted? : 汚染状態を調べる
untaint : オブジェクトの汚染を除く。
参考: https://docs.ruby-lang.org/ja/latest/doc/spec=2fsafelevel.html