安全なプログラムの作り方

42
安全なプログラムの作り方 Secure Programming 西山和広 (@znz) Good-Day Inc. Powered by Rabbit 0.9.2

Upload: kazuhiro-nishiyama

Post on 28-May-2015

18.928 views

Category:

Technology


1 download

DESCRIPTION

http://rubykaigi.org/2011/ja/schedule/details/17M01 での発表資料です。

TRANSCRIPT

Page 1: 安全なプログラムの作り方

安全なプログラムの作り方Secure Programming

西山和広 (@znz)Good-Day Inc.

Powered by Rabbit 0.9.2

Page 2: 安全なプログラムの作り方

Agendaprologue

具体例

まとめ

1/41

Page 3: 安全なプログラムの作り方

prologue (1) はじまりまだ javascript もなかった頃の話

掲示板 (伝言板) CGI がありました(twitter をもっと簡易にしたようなもの)

2/41

Page 4: 安全なプログラムの作り方

prologue (2) 掲示板 CGIHTML のタグを解析して A とか B とかHTML の構造が崩れないものを許可

それ以外のタグ HTML とか HEAD とかBODY とかはエスケープ

エスケープするのは、ちゃんとした HTML を出力するため

3/41

Page 5: 安全なプログラムの作り方

prologue (3) 余談hidden で埋め込んだ文字の文字コードで他のフィールドの文字コード推定もうまくいかないブラウザがあるとか

出力した HTML と同じ文字コードでフォームの内容も送ってくると思ったら、そうじゃないブラウザがあるとか

そういう理由で文字コードは結局自動判別しかなかったというのはまた別のお話

4/41

Page 6: 安全なプログラムの作り方

prologue (4) 変化javascript が普及

onclick などが指定できるようになる

javascript: というリンクも使えるようになる

問題がなかったはずの掲示板が何も変えていないのに脆弱性のあるものになった

5/41

Page 7: 安全なプログラムの作り方

結論永遠に安全なアプリケーションは困難

6/41

Page 8: 安全なプログラムの作り方

対策一般に

入力はちゃんとチェックする

出力はちゃんとエスケープする

多層の対策をするなど

その他環境の変化に注意する

7/41

Page 9: 安全なプログラムの作り方

脆弱性じゃないものが脆弱性になったといえば

Page 10: 安全なプログラムの作り方

URL 入力欄ブラウザの高機能化で脆弱性になったものとして URL 入力

問題点「javascript:」で始まる URL で javascript が実行できる

対策外部へのリンクを意図した入力欄なら「 http: 」または「 https: 」で始まる URL のみ許可

9/41

Page 11: 安全なプログラムの作り方

逆に昔からダメだった例として

Page 12: 安全なプログラムの作り方

GET は冪等に (1)昔から言われていたこと

GET で状態を変化させてはいけない

GET は冪等 (同じリクエストには同じ応答) であるべき

11/41

Page 13: 安全なプログラムの作り方

GET は冪等に (2)HTTP のリクエストメソッドは GET, POST, HEAD など

GET や HEAD は何度リクエストを送っても同じ結果を返すべき (羃等)

書き込みや削除には POST (や REST 風ならPUT や DELETE など) を使うべき

GET で削除できるようにしていたら検索エンジンのクローラーに消されたという話も。

12/41

Page 14: 安全なプログラムの作り方

べきといえば

Page 15: 安全なプログラムの作り方

例えば、/tmp を避け

Page 16: 安全なプログラムの作り方

/tmp を避ける (1)/tmp 以下を直接使うのは避けるべき

一時ファイルを作るときなど

特に固定のファイル名で作るのは危険

問題の例symlink attack の危険

Windows で困る

15/41

Page 17: 安全なプログラムの作り方

/tmp を避ける (2)代わりに

Rails なら Rails.root+'tmp' 以下

一般には tmpdir や tempfile などの標準添付ライブラリ

16/41

Page 18: 安全なプログラムの作り方

るびまの記事参照Rubyist Magazine - 標準添付ライブラリ紹介【第 15 回】 tmpdir, tempfile http://jp.rubyist.net/magazine/?0029-BundledLibraries

17/41

Page 19: 安全なプログラムの作り方

tmpdir と tempfile歴史的理由により

tmpdir は e なしの tmp

tempfile は e ありの temp

覚え方tmpdir は dir が 3 文字なので tmp も 3 文字

tempfile は file が 4 文字なので temp も 4 文字

18/41

Page 20: 安全なプログラムの作り方

/tmp といえば

Page 21: 安全なプログラムの作り方

パーミッションは最小限に2006 年の JVN#27365476: the Minnu's filer2 において、使用中のユーザの権限で別のユーザに任意の Ruby スクリプトを実行される脆弱性 http://jvn.jp//jp/JVN27365476/index.html

/tmp 以下に作成される外部との連携用の Unix Socket が誰でも読み書きできるパーミッションになっていた

ファイルを作成するときは umask などに注意

20/41

Page 22: 安全なプログラムの作り方

パーミッションとい

えば

Page 23: 安全なプログラムの作り方

hiki 0.8.6 の脆弱性2007 年の JVN#05187780: Hiki において任意のファイルが削除可能な脆弱性 http://jvn.jp//jp/JVN05187780/index.html

設置する人が出来るセキュリティ向上手段 : ファイルの所有者と実行ユーザを分離

権限一緒 : hiki 本体も削除可能

権限分離 : データしか削除できない

22/41

Page 24: 安全なプログラムの作り方

hiki 0.8.6 の脆弱性の詳細hiki/session.rb の

if /[0-9a-f]{16}/ =~ session_id

が問題で

if /\A[0-9a-f]{16}\z/ =~ session_id

に修正

23/41

Page 25: 安全なプログラムの作り方

正規表現といえば

Page 26: 安全なプログラムの作り方

改行と正規表現 (1)^ $ と \A \z (\Z) を使い分ける

^ : 行頭

$ : 行末

\A : 文字列の先頭

\z : 文字列の末尾

\Z : 文字列の末尾か末尾の改行の直前

25/41

Page 27: 安全なプログラムの作り方

改行と正規表現 (2)^ $ と \A \z (\Z) を使い分ける

Perl では /m を付けない限り ^ $ は文字列の先頭と末尾

Ruby で同じ意味を持つものは \A と \Z

入力全体をチェックするに使うのは ^ $ ではなく \A \z

先ほどの hiki の例で ^ $ を使うと "任意\n#{session_id}\n任意" のように回避できてしまう

26/41

Page 28: 安全なプログラムの作り方

正規表現と文字予期しない文字を通してしまうと危険

何が何にマッチするのか知っておくのは重要

たとえば 英数字なら\w は 1.9 だと 'あ' などにはマッチしない

[A-Za-z0-9] などのように書く方が確実

用途に応じて [[:alnum:]] とか \p{Alnum} とか

27/41

Page 29: 安全なプログラムの作り方

任意の正規表現ユーザ入力から正規表現を作る場合

任意のユーザ入力から正規表現を生成するのは危険

パターンによっては DoS 可能だと思っておいた方が良い

28/41

Page 30: 安全なプログラムの作り方

任意の入力といえば

Page 31: 安全なプログラムの作り方

ログ出力 (1)WEBrickにエスケープシーケンス挿入の脆弱性 http://www.ruby-lang.org/ja/news/2010/01/10/webrick-escape-sequence-injection/

コンソールへの任意の出力を許すのは危険らしい

30/41

Page 32: 安全なプログラムの作り方

ログ出力 (2)RVM がプロジェクトごとの .rvmrc ファイルを確認のため表示する

2011 年の JVN#30414126: Ruby Version Manager におけるエスケープシーケンスインジェクションの脆弱性 http://jvn.jp//jp/JVN30414126/index.html

31/41

Page 33: 安全なプログラムの作り方

ログ出力 (対策)対策

外部入力をログ出力するときには String#dump などを使ってバイナリのまま出力しない

コンソールへの出力でも外部入力を含んだりバイナリを含む場合はエスケープすべき

32/41

Page 34: 安全なプログラムの作り方

エスケープといえば

Page 35: 安全なプログラムの作り方

エスケープは適切にエスケープは出力先に合わせた適切な場所で適切なものを

HTML の属性値なら '' ではなく "" でくくる

エスケープ結果にあった文脈を用意することも必要

<tag attr='#{CGI.escapeHTML(value)'/> では意味がない

javascript のエスケープをしてから HTML のエスケープをするなど多段のエスケープが必要なことも

34/41

Page 36: 安全なプログラムの作り方

多段のエスケープとい

えば

Page 37: 安全なプログラムの作り方

コマンドライン処理 (1)起動元プログラムの中での文字列としてのエスケープ

シェルを経由するならシェルが処理するエスケープ

最終的に実行しようとしているプログラムのコマンドライン処理に対するエスケープ

36/41

Page 38: 安全なプログラムの作り方

コマンドライン処理 (2)コマンドラインを起動するとき

出来るだけ複数引数の system を使う

シェルのエスケープによる面倒は避ける

例: system("svn", "ci", "-m", message, "--", *files)

37/41

Page 39: 安全なプログラムの作り方

コマンドライン処理 (3)チェックポイント

ファイル名に空白が入っていても大丈夫か?

ファイル名が - で始まっていても大丈夫か?

ファイル名に ['"*] などの記号が入っていても大丈夫か? (そもそもそういうファイル名を許可するのか?)

NUL 文字 (\0) が入っていても大丈夫か?

38/41

Page 40: 安全なプログラムの作り方

コマンドライン処理 (4)受け取る側

シェルやシェルスクリプトから起動するときにちゃんと引数を渡せているか?

「alias p='ruby -e "p ARGV" --'」のようなエイリアスを作る

「foo='foo bar'; p $foo; unset foo」などで確認

39/41

Page 41: 安全なプログラムの作り方

まとめ永遠に安全なアプリはほぼ不可能

Web アプリならブラウザなど外部環境の変化

某キャリアの SSL ゲートウェイのような外部要因

入出力は特に気を付ける入力はチェックする

出力はちゃんとエスケープする

ファイル名やパーミッションにも気を付ける

40/41

Page 42: 安全なプログラムの作り方

Questions?