ikepyonのだめ人間日記

セキュリティに関することを書いていく予定。

IPAのセキュアプログラミング講座は微妙

改めて読んでみたんだけど、微妙な所がちらほら。

CSRF対策にSSL

SSL/TLSの使用

リクエスト強要(CSRF)対策に用いる照合情報は、他者に傍受されると都合が悪い。また、リクエスト強要(CSRF)対策が必要となる場面においては、ユーザやWebアプリケーションにとって重要なデータが送受信されることが十分考えられる。したがって、この場面においては、SSLあるいはTLSの使用が不可欠である。

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/301.html

実際の所SSLを使用するかどうかは、「通信先が確実に正しいことを保障する必要があるかどうか」と「通信内容を盗聴から守らなければならないほど重要か」と言うことを考慮して初めて判断するべきことだと思う。
確かに、CSRF対策に用いる照合情報は重要だろうけど、SSLを使わなければならないほどのものでもないだろう。
ところが、この文章を読む限りCSRF対策にSSLが必要と読める。そんなわけ無いと思うのだけど・・・
(追記)
誤解があることがあるようなので、処理が重要だからSSLを使う、CSRF対策をおこなうというのはありだと思う。しかし、CSRF対策に用いる照合情報が重要だからSSLを使うという訳ではない。最初の1文が余計だと思う。

セッションIDをSSLで守る

(1) セキュアプロトコルで守るべきもの

https:のセキュアプロトコルで守るべきものには次の2種類がある。

1. ユーザが目にする重要情報
2. セッションID

パスワード、個人情報、クレジットカード番号、ショッピング明細等、[1]に該当する情報が他人に漏れてはならないのは分かりやすい。

しかし、ユーザの目に触れないところでWebアプリケーションは [2]のセッションIDをブラウザとやり取りしている。

セッションIDが第三者に知られれば、セッション乗っ取りが生じるおそれがある。

暗号通信でセッションIDの秘密を守ることも、忘れてはならない対策のひとつである。

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/304.html

これも先のに同じ。セッションIDは確かに重要だ。しかし、それはアプリケーションが取り扱う情報や、処理が重要だから漏れるとまずいからである。何でもかんでもSSL使えばいいってものじゃない。セッションの乗っ取りについて言えば、SSLで通信を暗号化してるかどうかじゃない。SSLを使うかどうかは取り扱う情報や処理が重要かどうかで判断すべきで、セッション管理をしているかどうかではない。

セッションフィクサーション対策の検知方法

(1) クライアントIPアドレスの変化

セッションの途中でクライアントIPアドレスに変化が生じたらセッション乗っ取りを警戒する。
過剰検知: 次の場合は過剰検知である

* ふたつあるいは3つのIPアドレスを交互に使用するようになっているゲートウェイを通じてアクセスしてくるクライアント

検知の限界: 次の場合は攻撃者と正規ユーザの見分けがつかない

* 攻撃者と正規ユーザが同一のプロキシを通じてアクセスしてきている場合
* 攻撃者が正規ユーザのネットワークアクセスを妨害し、IPアドレスを乗っ取っている場合

(2) User-Agentリクエストヘッダの変化

セッションの途中でUser-Agentリクエストヘッダの値に変化が生じたらセッション乗っ取りを警戒する。
検知の限界: 次の場合は攻撃者と正規ユーザの見分けがつかない

* 攻撃者はスクリプト注入等を用いて正規ユーザのセッションIDのみならず、ブラウザのバージョン文字列も取得し、それをリクエストとともに送って来ている場合

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/306.html

補助的な対策と言うことなので、実装するかどうかはその判断によるのだけど、これらも微妙。
IPアドレスが変わったらというのは、弊害の方が大きくないか?プロキシ通している場合なんて結構ありそうだし。
User-Agentは実装することで、それほど大きな効果が得られるとは思えない。実際、攻撃する側からすれば、よく使われているブラウザのUser-Agentを使うことで、簡単に回避できるだろう。確かにこれを実装することで、敷居は少しあがるに違いない。でも、その効果は?というとそれほど無いと思う。実装した場合に必要なコストと効果を考えると、やっても良いけど、やらなくてもいいんじゃね?というレベルだと思う。

まあ、これらは先にも書いたが補助的な対策ということなので、実装するしないは実装者の判断なんだけど、このような機能を実装するのであれば、もっと先にやるべきことがあるんじゃないかなと思う。

(2) 重要な画面の直前のパスワード入力

重要な会話処理を開始する際、まずパスワードの入力を求め、正しいパスワードが提示されなければ情報表示や処理を行わないようにする。例えば、次のような場面においてである。:

* 個人情報・クレジットカード番号等、重要な情報を表示させて編集する場面
* 代金支払いの最終的な意思表示等、重要な意思決定を行う場面
* サイトの中における身分の変更、退会 等

後、些細な所だけど、格納されているクレジットカード番号を表示するというのがあるが、そもそもクレジットカード番号って全て表示しないとダメか?一部だけでいいだろう。まずはそちらを検討すべきだと思うのだけど、そういった記述がない。こういったことって要件定義や設計の段階で考慮すべきことだろうと思う。

想定外ナビゲーション

もたらされる被害

どのような現象が生じるかはそれぞれのWebアプリケーションによって異なるが、想定外のナビゲーション操作が行われたとき、プログラムが誤作動して次の被害を生じるおそれがある。

1. データのインテグリティが損なわれる
適切な順序でデータが組み立てられない状態でデータベース等への書き込みが行われた場合、データが不完全な形で書き換えられ、インテグリティ(一貫性、完全性)が損なわれるおそれがある。
2. 情報漏えいが生じる
検索条件が適切に揃わない状態でデータの検索が行われた場合、本来開示すべきでない情報をWebページに表示してしまうおそれがある。

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/307.html

引き起こされる被害に対して、対策がちぐはぐだと思う。確かにこれらで、想定外の画面遷移は防げるかもしれない。でも、これらの被害って、そういうもので防ぐんじゃないんじゃない?データの完全性が保証できないなら、データが不備な時点で、データの書き込みをすべきじゃないだろう。情報漏洩についてもそう。
これは被害例がまずいのであって、対策自体はまあ、そこそこいいかな?

Webサーバからのファイル流出対策

# 対策5
標準的な拡張子の使用。インクルードファイルに与える拡張子には、スクリプトの標準的な拡張子(PHPであれば.php等)を用いる。非標準の拡張子(.inc、.pm等)を用いない [要因4]
# 対策6
非標準拡張子の動作の定義。インクルードファイルに .inc のような標準では動作が定義されていない拡張子を与えるのであれば、WebサーバもしくはWebアプリケーションサーバにおいて、この拡張子をもつファイルの内容をブラウザに開示するのではなくスクリプトとして処理するよう定義する [要因5]

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/401.html

そもそも、インクルードファイルは直接呼び出す必要がなければ、Webルート上に置かない方がよいと思う。拡張子をどうかするより、先にそういったファイル配置の検討をすべきだと思う。

プログラムからのファイル流出対策

(1) 慎重な設計・実装

ファイル名をパラメータとして受け取るプログラムは要注意プログラムである。そのようなプログラムをWebサイトに置くことを決めた場合は、経験豊かな技術者を割り当てて、慎重に設計・実装する。

(1) Unix, GNU/Linuxの場合

* 可能なら、ファイル名パラメータの仕様として、ディレクトリ修飾のあるファイル名(「/」を含むパス名)を禁止するものとし、その仕様に沿った入力検査を行う
* 「/」ではじまるパス名(絶対パス名)を受理しない
* 「../」(親ディレクトリ修飾)を含むパス名を受理しない

(2) Windowsの場合

* 可能なら、ファイル名パラメータの仕様として、ディレクトリ修飾のあるファイル名(「/」もしくは「\」を含むパス名)を禁止するものとし、その仕様に沿った入力検査を行う
* 「英字:\」ではじまるパス名(絶対パス名)を受理しない
* 「\」ではじまるパス名(絶対パス名のバリエーション)を受理しない。
Windowsにはプレフィックス「\\?\」を用いた「\\?\c:\foo.bar」のようなパス名表記があり「c:\foo.bar」とほぼ同じ意味をもつ。そのようなパス名を排除する
* 「../」もしくは「..\」(親ディレクトリ修飾)を含むパス名を受理しない
* ドライブレターの直後《以外》に「:」を含むパス名を受理しない。Windowsファイルシステム形式のひとつNTFSのパス名には「:」を用いて副次ストリーム名を示す表記法があり、それを用いたパス名を排除する。例えば、「foo.dat::$DATA」は「foo.dat」と同じ意味をもつ。

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/402.html

ええと、「慎重に設計・実装する」って精神論ですか?それが出来ていないから脆弱性があると思うのだけど。あと、対策にファイルパスの正規化してから、ファイルが意図しているパスの配下かどうかチェックするというのが根本的な対策では無いかと思うのだけど。
それに使用するファイルがある程度決められているのであれば、該当するファイル名を配列に格納しておき、その配列の添え字を指定するようにすれば良いと思う。
どちらを使うかはアプリの仕様によるので、場合に応じた選択をすべきだと思う。

デバッグオプション対策

1. デバッグ出力はすべてサーバ内のログファイルに書き出すようにする
2. 開発時はHTMLページ上へのデバッグ出力機能を持たせるが、プログラムが本番サーバに搭載される前にすべてソースコード上から削除する
3. 開発時はHTML出力、本番運用時はログファイル出力となるようなデバッグ出力APIと、大本の切り替え機構を設ける

加えて、これらの対策が確実に行われていることを確認する運用を行う。

4. 本番サイトにWebプログラムを設置する前にソースコードを検査し、デバッグオプションあるいはHTML上にデバッグ出力する設定が残存していれば、それを削除・修正しないかぎり当該プログラムの本番サイト上への設置を許さない。

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/web04.html

そもそもデバッグオプションってパラメータで渡す必要があるものなのか疑問。デバッグオプションが必要なのであれば、設定ファイルなどでデバッグオプションのon/offを指定するべきだと思う。それを書いていない所でまずいと思う。
加えてデバッグ出力機能を削除って・・・デバッグ出力機能を削除したら、また最初からテストし直しじゃないのか?で、削除しすぎてバグが出たらまたテスト。本番サイトに上げるのにどれだけ手間かかるの?
テストが終わった段階でコードの修正はやってはいけないと思う。

コマンドインジェクション対策

使い方によっては、シェルを起動してコマンドを解釈させることになるAPIがあることを認識し、できればそれらのAPIを使わないようにする。
Perl

exec(), system(), `...`, qx/.../
なるべく使わない。
open(h, "|{command}"), open(h, "{command}|")
なるべく使わない。open()の代わりにsysopen()を使う。
open()を使うのであれば、入力・出力を示す < や > の記号をパス名に添えて、open(HANDLE, "<$pathname") のようにする。

PHP

exec(), passthru(), proc_open(), shell_exec(), system()
なるべく使わない。

Python

os.system(), os.popen()
なるべく使わない。

Ruby

exec(), system(), `...`
なるべく使わない。exec()でも、引数が1つであってその中に特殊記号 * ? {} [] <> () ~ & | \ $ ; ' ` " \n のいずれかが含まれているとシェルが起動される。
open("|{command}", mode, perm),
open("|-{command}", mode, perm)
open()はなるべく使わない。open()を使うのであれば、ファイル名引数(第1引数)に記号「|」が含まれないようにする。


(2) 特殊記号の排除

別のプログラムに与えるパラメータに用いる文字種を英数字のみ等安全なものに限定し、検査してから渡す。

bashの場合、次の特殊記号は別コマンドの実行に使われるものとして特に警戒する。

「;」「|」「&」「`」「(」「)」。

また、次の特殊記号もファイルへのアクセスが起こったりコマンドの意味が変わり得るので警戒する。

「$」「<」「>」「*」「?」「{」「}」「[」「]」「!」

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/501.html

「なるべく使わない」ってどういう場合だったら使っても良いのか?すごく曖昧。使って良い場合を明確にしてほしい。
例えば、コマンドの引数に入力データを使わない場合は使っても良いという風にしてくれればいいのに。
「特殊記号の排除」に至っては、これらを使いたい場合どうすればいいのか?ってのが分からない。例えば、引数を「"」でくくって、「"」は「\"」にするとか書いといて欲しい。

SQLインジェクション対策

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/502.html
ほぼ良いんだけど、prepared statementを使えない場合とか、通常のエスケープ処理が使えない場合、例えば、自由検索で、動的に検索項目を変えたい場合の対策が書かれているとうれしいなぁ。

XSS対策

(3) HTMLデータの排除
入出力データとしてタグを含むHTMLデータを扱わずに済む場合
対策内容

Webアプリケーションの入出力データとしてタグを含むHTML文字列を扱わない。
適用できないケース

ただし、掲示板、ブログ、ショッピングカタログ構築キット等、HTMLタグを含む文字列データの取り扱いが不可避のアプリケーションもあり得る。
その場合は以下の入出力データとしてタグを含むHTMLデータを扱う必要にせまられている場合の対策を実施する。
入出力データとしてタグを含むHTMLデータを扱う必要に迫られている場合

そのような場合は、HTMLデータからのスクリプトを排除する。 具体的には、次のような対策を行う。
対策内容

タグを含むHTMLドキュメントまたはその一部をアプリケーションの入出力データとして取り扱う場合、入力されたHTMLドキュメントを構文解析し、スクリプトを含んでいる場合データとして受け付けないか、スクリプトを削除してから使用する。

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/601.html

スクリプトを含んでいるかどうかの具体的な確認方法は何だろう?場合によって異なるから書いてないのだろうけど、実装しようとすれば、どうすればいいのか分からない。具体例をいくつか出してほしい物だ。

まとめ

とまあ、見てきたけれど、どうも今一。全体的に、絶対にしなければいけないことと、出来ればした方が良いことの違いが明確でないし、具体的な対策が明確になっているものとなっていないものがあり、かなりまちまち。
それに脆弱性の発生原因が個々の脆弱性に止まっていて、根本的な仕組みが解説されていない。その結果、xPathインジェクションや、LDAPインジェクションといった脆弱性対策が出来なくなる気がする。
そもそも、アプリケーションのセキュリティ対策には、必ずやるべきものと、やるかどうか判断を要するものがあると思う。やるかどうか判断を要するものは、やることで効果が得られるほど以上にコストがかかるかどうかや、取り扱う機能、データの重要性に応じた対策かどうかを検討する必要があるものがあると思うが、そういったことを一切なしに全部やれ、あるいはこれはやった方が良いとか書かれているように思う。
実際、そこまでやればいいけど、やるほどのものか?とかやることによって効果が得られる以上のコストがかからないか?というのも混じっている。
これらを明確にしてほしいなぁ。