LoginSignup
6
1

More than 5 years have passed since last update.

ルービー3分クッキング「今日はオブジェクトを汚染していきます」

Last updated at Posted at 2017-07-14

「オブジェクトを汚染する」という厨二感あふれるワードに惹かれて、
taintに触れてみました。
畏れ多き、
rubyのセキュリティ機構のご報告になります!:chicken:
タイトルの3分は関係ありません。:clock:

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

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1