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

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




Powered by Rabbit 0.9.2
Agenda
 prologue

 具体例

 まとめ




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

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




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

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

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



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

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

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

                        4/41
prologue (4) 変化
  javascript が普及
    onclick などが指定できるようになる

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

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



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




                    6/41
対策
 一般に
     入力はちゃんとチェックする

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

     多層の対策をするなど

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



                      7/41
脆弱性じゃない
ものが脆弱性に
なったといえば
URL 入力欄
 ブラウザの高機能化で脆弱性になったものと
 して URL 入力

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

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

                                     9/41
逆に昔から
ダメだった
 例として
GET は冪等に (1)
 昔から言われていたこと
   GET で状態を変化させてはいけない

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




                           11/41
GET は冪等に (2)
 HTTP のリクエストメソッドは GET, POST,
 HEAD など
   GET や HEAD は何度リクエストを送っても同じ
   結果を返すべき (羃等)

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

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

                                12/41
べきと
いえば
例えば、/
tmp を避け
   る
/tmp を避ける (1)
 /tmp 以下を直接使うのは避けるべき
   一時ファイルを作るときなど

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

 問題の例
   symlink attack の危険

   Windows で困る



                        15/41
/tmp を避ける (2)
 代わりに
   Rails なら Rails.root+'tmp' 以下

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




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




                               17/41
tmpdir と tempfile
 歴史的理由により
   tmpdir は e なしの tmp

   tempfile は e ありの temp

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

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


                                   18/41
/tmp と
 いえば
パーミッションは最小限に
 2006 年の JVN#27365476: the Minnu's
 filer2 において、使用中のユーザの権限で別
 のユーザに任意の Ruby スクリプトを実行さ
 れる脆弱性 http://jvn.jp//jp/
 JVN27365476/index.html
   /tmp 以下に作成される外部との連携用の Unix
   Socket が誰でも読み書きできるパーミッションに
   なっていた

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

                                20/41
パーミッ
ションとい
  えば
hiki 0.8.6 の脆弱性
 2007 年の JVN#05187780: Hiki において
 任意のファイルが削除可能な脆弱性 http://
 jvn.jp//jp/JVN05187780/index.html

 設置する人が出来るセキュリティ向上手
 段 : ファイルの所有者と実行ユーザを分離
   権限一緒 : hiki 本体も削除可能

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



                                 22/41
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
正規表現
といえば
改行と正規表現 (1)
 ^ $ と A z (Z) を使い分ける
   ^ : 行頭

   $ : 行末

   A : 文字列の先頭

   z : 文字列の末尾

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




                           25/41
改行と正規表現 (2)
 ^ $ と A z (Z) を使い分ける
   Perl では /m を付けない限り ^ $ は文字列の先
   頭と末尾

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

 入力全体をチェックするに使うのは ^ $ で
 はなく A z
   先ほどの hiki の例で ^ $ を使うと "任意n#
   {session_id}n任意" のように回避できてしまう

                               26/41
正規表現と文字
 予期しない文字を通してしまうと危険
  何が何にマッチするのか知っておくのは重要

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

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

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


                                 27/41
任意の正規表現
 ユーザ入力から正規表現を作る場合
  任意のユーザ入力から正規表現を生成するのは危
  険

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




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

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




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

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




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

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




                           32/41
エスケープ
 といえば
エスケープは適切に
 エスケープは出力先に合わせた適切な場所で
 適切なものを
  HTML の属性値なら '' ではなく "" でくくる

  エスケープ結果にあった文脈を用意することも必
  要
    <tag attr='#{CGI.escapeHTML
    (value)'/> では意味がない

  javascript のエスケープをしてから HTML のエ
  スケープをするなど多段のエスケープが必要なこ
  とも
                                  34/41
多段のエス
ケープとい
  えば
コマンドライン処理 (1)
 起動元プログラムの中での文字列としてのエ
 スケープ

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

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



                    36/41
コマンドライン処理 (2)
 コマンドラインを起動するとき
  出来るだけ複数引数の system を使う

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

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




                                            37/41
コマンドライン処理 (3)
 チェックポイント
  ファイル名に空白が入っていても大丈夫か?

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

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

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



                             38/41
コマンドライン処理 (4)
 受け取る側
  シェルやシェルスクリプトから起動するときに
  ちゃんと引数を渡せているか?

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

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




                                      39/41
まとめ
 永遠に安全なアプリはほぼ不可能
  Web アプリならブラウザなど外部環境の変化

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

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

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

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

                           40/41
Questions?

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

  • 1.
    安全なプログラムの作り方 Secure Programming 西山和広 (@znz) Good-Day Inc. Powered by Rabbit 0.9.2
  • 2.
  • 3.
    prologue (1) はじまり まだ javascript もなかった頃の話 掲示板 (伝言板) CGI がありました (twitter をもっと簡易にしたようなもの) 2/41
  • 4.
    prologue (2) 掲示板CGI HTML のタグを解析して A とか B とか HTML の構造が崩れないものを許可 それ以外のタグ HTML とか HEAD とか BODY とかはエスケープ エスケープするのは、ちゃんとした HTML を出力するため 3/41
  • 5.
    prologue (3) 余談 hidden で埋め込んだ文字の文字コードで他 のフィールドの文字コード推定もうまくいか ないブラウザがあるとか 出力した HTML と同じ文字コードでフォー ムの内容も送ってくると思ったら、そうじゃ ないブラウザがあるとか そういう理由で文字コードは結局自動判別しか なかったというのはまた別のお話 4/41
  • 6.
    prologue (4) 変化 javascript が普及 onclick などが指定できるようになる javascript: というリンクも使えるようになる 問題がなかったはずの掲示板が 何も変えていないのに 脆弱性のあるものになった 5/41
  • 7.
  • 8.
    対策 一般に 入力はちゃんとチェックする 出力はちゃんとエスケープする 多層の対策をするなど その他 環境の変化に注意する 7/41
  • 9.
  • 10.
    URL 入力欄 ブラウザの高機能化で脆弱性になったものと して URL 入力 問題点 「javascript:」で始まる URL で javascript が実 行できる 対策 外部へのリンクを意図した入力欄なら「 http: 」 または「 https: 」で始まる URL のみ許可 9/41
  • 11.
  • 12.
    GET は冪等に (1) 昔から言われていたこと GET で状態を変化させてはいけない GET は冪等 (同じリクエストには同じ応答) であ るべき 11/41
  • 13.
    GET は冪等に (2) HTTP のリクエストメソッドは GET, POST, HEAD など GET や HEAD は何度リクエストを送っても同じ 結果を返すべき (羃等) 書き込みや削除には POST (や REST 風なら PUT や DELETE など) を使うべき GET で削除できるようにしていたら検索エンジ ンのクローラーに消されたという話も。 12/41
  • 14.
  • 15.
  • 16.
    /tmp を避ける (1) /tmp 以下を直接使うのは避けるべき 一時ファイルを作るときなど 特に固定のファイル名で作るのは危険 問題の例 symlink attack の危険 Windows で困る 15/41
  • 17.
    /tmp を避ける (2) 代わりに Rails なら Rails.root+'tmp' 以下 一般には tmpdir や tempfile などの標準添付ラ イブラリ 16/41
  • 18.
    るびまの記事参照 Rubyist Magazine -標準添付ライブラリ紹介 【第 15 回】 tmpdir, tempfile http:// jp.rubyist.net/magazine/?0029- BundledLibraries 17/41
  • 19.
    tmpdir と tempfile 歴史的理由により tmpdir は e なしの tmp tempfile は e ありの temp 覚え方 tmpdir は dir が 3 文字なので tmp も 3 文字 tempfile は file が 4 文字なので temp も 4 文 字 18/41
  • 20.
  • 21.
    パーミッションは最小限に 2006 年のJVN#27365476: the Minnu's filer2 において、使用中のユーザの権限で別 のユーザに任意の Ruby スクリプトを実行さ れる脆弱性 http://jvn.jp//jp/ JVN27365476/index.html /tmp 以下に作成される外部との連携用の Unix Socket が誰でも読み書きできるパーミッションに なっていた ファイルを作成するときは umask などに注意 20/41
  • 22.
  • 23.
    hiki 0.8.6 の脆弱性 2007 年の JVN#05187780: Hiki において 任意のファイルが削除可能な脆弱性 http:// jvn.jp//jp/JVN05187780/index.html 設置する人が出来るセキュリティ向上手 段 : ファイルの所有者と実行ユーザを分離 権限一緒 : hiki 本体も削除可能 権限分離 : データしか削除できない 22/41
  • 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
  • 25.
  • 26.
    改行と正規表現 (1) ^$ と A z (Z) を使い分ける ^ : 行頭 $ : 行末 A : 文字列の先頭 z : 文字列の末尾 Z : 文字列の末尾か末尾の改行の直前 25/41
  • 27.
    改行と正規表現 (2) ^$ と A z (Z) を使い分ける Perl では /m を付けない限り ^ $ は文字列の先 頭と末尾 Ruby で同じ意味を持つものは A と Z 入力全体をチェックするに使うのは ^ $ で はなく A z 先ほどの hiki の例で ^ $ を使うと "任意n# {session_id}n任意" のように回避できてしまう 26/41
  • 28.
    正規表現と文字 予期しない文字を通してしまうと危険 何が何にマッチするのか知っておくのは重要 たとえば 英数字なら w は 1.9 だと 'あ' などにはマッチしない [A-Za-z0-9] などのように書く方が確実 用途に応じて [[:alnum:]] とか p{Alnum} と か 27/41
  • 29.
    任意の正規表現 ユーザ入力から正規表現を作る場合 任意のユーザ入力から正規表現を生成するのは危 険 パターンによっては DoS 可能だと思っておいた 方が良い 28/41
  • 30.
  • 31.
    ログ出力 (1) WEBrickにエスケープシーケンス挿入の脆弱 性 http://www.ruby-lang.org/ja/ news/2010/01/10/webrick-escape- sequence-injection/ コンソールへの任意の出力を許すのは危険ら しい 30/41
  • 32.
    ログ出力 (2) RVMがプロジェクトごとの .rvmrc ファイ ルを確認のため表示する 2011 年の JVN#30414126: Ruby Version Manager におけるエスケープシーケンスイ ンジェクションの脆弱性 http://jvn.jp//jp/ JVN30414126/index.html 31/41
  • 33.
    ログ出力 (対策) 対策 外部入力をログ出力するときには String#dump などを使ってバイナリのまま出力しない コンソールへの出力でも外部入力を含んだりバイ ナリを含む場合はエスケープすべき 32/41
  • 34.
  • 35.
    エスケープは適切に エスケープは出力先に合わせた適切な場所で 適切なものを HTML の属性値なら '' ではなく "" でくくる エスケープ結果にあった文脈を用意することも必 要 <tag attr='#{CGI.escapeHTML (value)'/> では意味がない javascript のエスケープをしてから HTML のエ スケープをするなど多段のエスケープが必要なこ とも 34/41
  • 36.
  • 37.
    コマンドライン処理 (1) 起動元プログラムの中での文字列としてのエ スケープ シェルを経由するならシェルが処理するエス ケープ 最終的に実行しようとしているプログラムの コマンドライン処理に対するエスケープ 36/41
  • 38.
    コマンドライン処理 (2) コマンドラインを起動するとき 出来るだけ複数引数の system を使う シェルのエスケープによる面倒は避ける 例: system("svn", "ci", "-m", message, "--", *files) 37/41
  • 39.
    コマンドライン処理 (3) チェックポイント ファイル名に空白が入っていても大丈夫か? ファイル名が - で始まっていても大丈夫か? ファイル名に ['"*] などの記号が入っていても大 丈夫か? (そもそもそういうファイル名を許可する のか?) NUL 文字 (0) が入っていても大丈夫か? 38/41
  • 40.
    コマンドライン処理 (4) 受け取る側 シェルやシェルスクリプトから起動するときに ちゃんと引数を渡せているか? 「alias p='ruby -e "p ARGV" --'」のようなエイ リアスを作る 「foo='foo bar'; p $foo; unset foo」などで確認 39/41
  • 41.
    まとめ 永遠に安全なアプリはほぼ不可能 Web アプリならブラウザなど外部環境の変化 某キャリアの SSL ゲートウェイのような外部要因 入出力は特に気を付ける 入力はチェックする 出力はちゃんとエスケープする ファイル名やパーミッションにも気を付ける 40/41
  • 42.