Secure Programming for Linux and Unix HOWTO David A. Wheeler dwheeler@dwheeler.com 高橋聡 - 日本語訳 hisai@din.or.jp v2.962 Edition Copyright (C) 1999, 2000, 2001, 2002 by David A. Wheeler v2.962, 12 March 2002 この文書は、Linux および Unix システム上で安全なプログラムを書く際に必 要となる設計や実装について、そのガイドラインを提供します。遠隔のデータ を見るためのビューアーや Web アプリケーション(CGI スクリプトを含む)、ネ ットワーク・サーバ、setuid や setgid してあるプログラムが対象です。 C や C++、Java、Perl、PHP、Python、TCL、Ada95 個別のガイドラインも掲載し ます。 This book is Copyright (C) 1999-2002 David A. Wheeler. Permission is granted to copy, distribute and/or modify this book under the terms of the GNU Free Documentation License (GFDL), Version 1.1 or any later version published by the Free Software Foundation; with the invariant sections being ``About the Author'', with no Front-Cover Texts, and no Back-Cover texts. A copy of the license is included in the section entitled "GNU Free Documentation License". This book is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Table of Contents 1. はじめに 2. 背景 2.1. Unix や Linux、オープンソースもしくはフリーソフトウェアについ て 2.2. セキュリティの原則 2.3. なぜプログラマは危ないコードを書いてしまうのか 2.4. オープンソースはセキュリティに効果があるのか 2.5. 安全なプログラムの種類 2.6. 疑い深く、こだわりが強いことに価値がある 2.7. このドキュメントを書いた訳は ? 2.8. 設計と実装の指針についての情報源 2.9. その他のセキュリティ情報源 2.10. ドキュメントでの約束事 3. Linux と Unix のセキュリティ機能 3.1. プロセス 3.2. ファイル 3.3. System V IPC 3.4. ソケットとネットワーク接続 3.5. シグナル 3.6. Quota とリソースの制限 3.7. ダイナミックリンク・ライブラリ 3.8. Audit(監査) 3.9. PAM 3.10. Unix ライクなシステムに固有なセキュリティ拡張機能 4. 入力されるものすべてを検証すること 4.1. コマンドライン 4.2. 環境変数 4.3. ファイル・ディスクリプタ 4.4. ファイルの内容 4.5. Web ベースのアプリケーションの入力(特に CGI スクリプト) 4.6. その他の入力 4.7. 自然言語(ロカール)の選択 4.8. 文字のエンコード 4.9. サイトにまたがった悪意あるコンテンツ(Cross-site Malicious Content)を防ぐ 4.10. 再表示する可能性のある HTML や URI にはフィルタをかける 4.11. クエリ以外の実行に HTTP の GET 命令を使わせない 4.12. SPAM に対抗する 4.13. 入力時間と負荷レベルに制限をかける 5. バッファオーバーフローの回避 5.1. C や C++ の危険なところ 5.2. C と C++ でのライブラリによる解決策 5.3. C や C++ でのコンパイルによる解決 5.4. その他の言語 6. プログラムのインタフェースと内部構成をきちんとすること 6.1. 安全なプログラムを作るためには、ソフトウェア・エンジニアリング の原則に従うこと 6.2. インタフェースを安全に 6.3. データと制御を切り離す 6.4. 特権を最小限に 6.5. 1 つの構成要素の機能を最小限にする 6.6. setuid や setgid したスクリプトを使わない 6.7. 設定を安全にし、安全なデフォルトを使用する 6.8. 初期値を安全にロードする 6.9. フェイル・セーフ 6.10. 競合状態を避ける 6.11. 信頼できる経路だけ信じること 6.12. 高信頼パス(Trusted Path)を設ける 6.13. 内部で一貫性をチェックするコードを利用する 6.14. リソースを自己制御する 6.15. サイトにまたがって存在する悪意あるコンテンツを防ぐ 6.16. セマンティック攻撃の裏をかく 6.17. データの種類に気を配る 7. 他のリソースを利用する場合は慎重に 7.1. 安全なライブラリ・ルーチンだけを呼び出すこと 7.2. 正しい値でだけ呼び出す 7.3. メタキャラクタを扱う 7.4. プログラマ向けのインタフェースだけを呼び出す 7.5. システムコールの返り値はすべてチェックする 7.6. vfork(2)は使わない 7.7. 組込みコンテンツの読み込み時に発生する Web バグに対処する 7.8. 秘密にしたい情報は隠す 8. 情報はえりすぐってフィードバックする 8.1. フィードバックは最小限に 8.2. コメントはいれない 8.3. 出力が溢れたり、反応が遅い場合も対処する 8.4. データフォーマットを制御する(「書式文字列」) 8.5. 出力時に文字符号化を制御する 8.6. Include ファイルや設定ファイルへのアクセスを防ぐ 9. 言語固有の問題 9.1. C と C++ 9.2. Perl 9.3. Python 9.4. シェルスクリプト言語(sh と csh 系) 9.5. Ada 9.6. Java 9.7. TCL 9.8. PHP 10. 専門的な話題 10.1. パスワード 10.2. Web の認証 10.3. 乱数 10.4. ユーザ空間ではとりわけ秘密(パスワードや鍵)を守る 10.5. 暗号化アルゴリズムとプロトコル 10.6. PAM を使う 10.7. ツール 10.8. Windows CE 10.9. 監査記録を書き込む 10.10. 物理的な漏洩 10.11. その他 11. 結論 12. 参考文献 A. 履歴 B. おことわり C. ドキュメントのライセンスについて D. GNU Free Documentation License E. About the Author F. 日本語版謝辞 List of Tables 4-1. Legal UTF-8 Sequences List of Figures 1-1. プログラム概念図 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 1. はじめに 【訳註:聖書の訳は、日本聖書協会聖 書新共同訳から引用しました。原文の 参照元である NIV(New International Version)が改版されていたため、その 日本語版 (http://www.gospelcom.net/ ibs/bibles/japanese/) は用いません でした。以下同様です】 知恵ある人はひとりで勇士たちの町に 上りその頼みとする砦を落とすことも できる。 旧約聖書箴言 21 章 22 節 この文書は Linux および Unix システム上で安全なプログラムを書く際に必要 となる設計や実装について、そのガイドラインを提供します。この文書の意図 する、「安全なプログラム」とは、セキュリティの境界線上に位置し、そのプ ログラムとは異なるアクセス権限を持つ接続元からの入力を扱うプログラムで す。そのようなプログラムには、遠隔のデータを見るためのビューアーを使っ たものや、 Web アプリケーション(CGI スクリプトを含む)、ネットワーク・サ ーバ、setuid もしくは setgid してあるプログラムがあります。この文書では 、オペレーティングシステムのカーネル自体の修正は扱いませんが、これから 議論する原則はカーネルに対しても適用できる場合がよくあります。安全なプ ログラムをどのように作成するかについて、さまざまな情報源を調べ回った「 教訓」を元に、ガイドラインとして構成し直し、広範に適用できるようにしま した (著者の考えも加えています)。この文書はいくつかの言語、具体的には C や C++、Java、Perl、PHP、Python、TCL、 Ada95 に固有の手引きも記載してい ます。 この文書は、保証基準やソフトウェア・エンジニアリングの工程、品質保証か ら見た取り組みについては触れていません。そのような指標は大切ですが、既 にあちこちで議論されています。テストやピア・レビュー、コンフィギュレー ション管理、形式的な各種の方法がそれに当たります。セキュリティに関連し た開発に当たっての保証基準については、 the Common Criteria [CC 1999] や the Systems Security Engineering Capability Maturity Model [SSE-CMM 1999] に記載してあります。ソフトウェア・エンジニアリングの工程全般につ いては Software Engineering Institute's Capability Maturity Model for Software (SW-CMM) [Paulk 1993a, 1993b] や ISO 12207 [ISO 12207] を参照 してください。高品質なシステムについての国際標準については、ISO 9000 や ISO 9001 [ISO 9000, 9001] を参照してください。 この文書では、ある特定の環境にあるシステムやネットワークを安全に設定す る方法については論じません。安全な設定は、あるプログラムを安全に使用す るのに必須であることは明らかですが、安全に設定することを論じたドキュメ ントは他にもたくさんあります。 Unix ライクなシステムを安全に設定するこ とについて述べてある書籍には Garfinkel [1996]という素晴らしい作品があり ます。他にも、Anonymous[1998]というものがあります。また Web サイトでも 情報を得られます。たとえば http://www.unixtools.com/security.html 等で す。 Linux システムを安全に設定する情報については、さまざまなドキュメン トが利用可能です。Fenzi[1999] や Seifried[1999]、Wreski[1998]、Swan [2001]、 Anonymous[1999] がそれに当たります。 Geodsoft [2001] では OpenBSD をいかに強固にするかといったことに加えて、 Unix ライクなシステ ムに役に立つ示唆がたくさんあります。 Linux システム(つまるところ他の Unix ライクなシステムも)をターゲットにしているなら、Bastille Hardening Systemを調べるのも良いでしょう。このシステムは Linux オペレーティングシ ステムをより強固で厳重にしようとしています。さらに Bastille について知 りたいなら、 http://www.bastille-linux.org を見てください。General Public License (GPL) にしたがって自由に利用できます。 Windows 2000 がタ ーゲットなら Cox[2000] を見るのもよいでしょう。米国国家安全保障局(The U.S. National Security Agency(NSA)) は、セキュリティに関しての推奨ガイ ドを http://nsa1.www.conxion.com で整備しています。その中には、「60 Minute Network Security Guide」というものもあります。 コンピュータを設定するのは、セキュリティ管理の一部にしかすぎません。セ キュリティ管理は広範な内容をカバーしています。ウイルスへの対処方法やど のような組織的なセキュリティ・ポリシが必要で、事業継続計画はどうするの か、といったこと等を含んでいます。セキュリティ管理には国際的な基準とガ イドラインがあります。 ISO 13335 は全 5 部からなるテクニカル・レポート から構成され、セキュリティ管理の手引きになっています[ISO 13335]。また ISO/IEC 17799:2000 では作業標準を定義しています[ISO 17799]。規定した目 的は、「組織にあって、セキュリティ管理を企画、実行し、それを維持する責 任を負う立場の人間に推奨する情報を提供する」ことです(幅広い内容を扱って います。技術文書ではありません)。興味深いのは ISO/IEC 17799:2000 の意見 が分かれているところです。ベルギーやカナダ、フランス、ドイツ、イタリア 、日本、米国は採択に反対しました。議論についての詳細は、NIST(National Institute of Standards and Technology) の ISO/IEC 17799:2000 FAQ を見てく ださい。 The Commonly Accepted Security Practices & Recommendations (CASPR)( http://www.caspr.org)プロジェクトは、セキュリティ情報を集約し 、誰もが利用できるドキュメントの作成に取り組んでいます (誰もが将来の文 書の派生物を入手可能であり続けられるように、GNU FDL ライセンスとしまし た)。 この文書は読者の方がコンピュータのセキュリティ一般や、Unix ライクなシス テム、ネットワーク(特に TCP/IP ベース)、C 言語について理解していること を前提にしています。この文書には Linux や Unix でセキュリティを維持する のに必要なプログラミング・モデルの情報があります。 TCP/IP ベースのネッ トワークや安全なプロトコルを含むプロトコルの動作についてさらに知りたい なら、[Murhammer 1998] のような TCP/IP 全般についての資料を調べてくださ い。 この文書は Unix ライクなシステムを全て網羅しています。Linux をはじめ、 さまざまな系列の Unix を含んでいますが、特に Linux に焦点を当て、Linux に特化した情報を提供します。 Windows CE に焦点を当てたところもあります が、実際大部分の項目は特定のオペレーティングシステムに限定されません。 関連した情報でここで触れていない事項があれば、お知らせください。 この文書の原本は、http://www.dwheeler.com/secure-programs にあります。 この文書は Linux Documentation Project (LDP) http://www.linuxdoc.org の 一部でもあり、ミラーサイトがいくつか存在しています。ミラーにある LDP の コピーやディストリビューションにあるものは、原本よりも古いかもしれない ので注意してください。この文書について意見がいただけると助かりますが、 最新版をまず確認してから、送ってください。 This book is copyright (C) 1999-2001 David A. Wheeler and is covered by the GNU Free Documentation License (GFDL); 詳しくは、Appendix C や Appendix D を見てください。 Chapter 2 それでは Unix や Linux、セキュリティの背景について論じます。 Chapter 3 まず Unix と Linux のセキュリティ・モデル全般について論じてい ます。そのモデルは、セキュリティに関する属性とプロセスやファイルシステ ム等の操作について概観します。そして、この文書の要となる Linux と Unix システム上でアプリケーション開発をするに当たっての、設計と実装のガイド ラインが続きます。この文章は結論 Chapter 11 で締めくくり、その後に参考 文献一覧と付録がずらっと並びます。 プログラマの観点として重要と考える側面から、設計と実装についてのガイド ラインを分類します。プログラムは入力を受取り、データを処理し、他のリソ ースを呼び出し、出力を生成します。Figure 1-1はこれを図で表わしています 。つまり概念上、セキュリティ・ガイドラインはすべてこれらのカテゴリのど れかに当てはまります。さらに「データ処理」を専門的な話題に分類します。 その分野とは、プログラム内部の構造化への取り組み方(Chapter 6)、バッファ オーバーフロー(入力の問題として検討するケースもあります)の回避、言語に 固有の情報です。章の構成は、順序立てて理解しやすいようにしています。以 上の考えにもとづき、このガイドラインの章立ては次のようになります。入力 されるものすべてを検証する(Chapter 4)、バッファオーバーフローの回避 ( Chapter 5)、プログラムのインタフェースと内部構成をきちんとすること( Chapter 6)、他のリソースを利用する場合は慎重に(Chapter 7)、情報はえりす ぐってフィードバックする (Chapter 8)、言語固有の問題 (Chapter 9)、そし て最後にどのように乱数を得るかといった、専門的な話題(Chapter 10)を扱う こととします。 Figure 1-1. プログラム概念図 [program] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 2. 背景 命じて調べさせたところ、その都は昔 から歴代の王に対して反抗し、反逆と 反乱を起こしたことが確認された。 旧約聖書エズラ記 4 章 19 節 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1. Unix や Linux、オープンソースもしくはフリーソフトウェアについて 2.1.1. Unix 1969 から 1970 年にかけて、Kenneth Thompson 氏と Dennis Ritchie 氏らが AT&T ベル研究所において、ほとんど使われていない PDP-7 上で、ちょっとし たオペレーティングシステムを開発しはじめました。そのオペレーティングシ ステムはまもなく Unix という洗礼名を授かりました。先に誕生した MULTICS と呼ばれたオペレーティングシステムをもじって付けられました。 1972 から 1973 年にかけて、C 言語でシステムを書き換え、これによって思いがけない歴 史を歩むことになります。つまりこの決断によって Unix はオリジナルのハー ドウェアから独立し、さらに生き長らえる最初のオペレーティングシステムと なりました。 Unix には他にも新機軸の機能が加わりました。これはベル研究 所とアカデミックなコミュニティとの相乗効果のおかげでした。 1979 年に「 seventh edition」 (V7)と呼ばれるバージョンの Unix がリリースされ、現存 している Unix システムすべての始祖が誕生しました。 この時点から Unix はいささか混迷期に入り込みます。アカデミックな世界で は、バークレイ校がリーダーとなり Berkeley Software Distribution (BSD)と 言われる系列を開発しました。一方 AT&T は Unix を「System III」という名 で開発し続け、それが後に「System V」となりました。 1980 年の後半から 1990 年の前半にかけて、この 2 つのメジャーな系列間で「戦争」が勃発しま した。その後何年もそれぞれの系列は、相手の重要な機能の多くを取り入れあ いました。商用である System V が「標準化戦争」に打ち勝ち(そのインタフェ ースのほとんどが公式の標準になりました)、ハードウェアベンダーの大部分が AT&T の System V に移行しました。しかし、System V は BSD の革新的な技術 をたくさん組み込んでいて、結局は 2 つの支流を 1 つに統合したシステムと なりました。 BSD 派は生き長らえ、研究分野や PC ハードウェア用、専用サー バ(たとえば Web サイトは BSD の流れを汲むシステムを使っている場合が多 い)として広く利用されるようになりました。 こうして seventh edition を起源とする多彩なバージョンの Unix が存在する 結果になりました。 Unix の大部分のバージョンは、ハードウェアベンダーが 所有し、それぞれでメンテナンスをしています。たとえば Sun の Solaris は System V 系列です。 BSD 系列の Unix の内 3 つのバージョンは、オープンソ ースになりました。 FreeBSD(PC タイプのハードウェアに簡単にインストール できることを目指す)や NetBSD(各種 CPU アーキテクチャ上で動作することを 目指す)、NetBSD の系列になる OpenBSD(セキュリティに重点を置く)がそれに 当たります。 Unix の歩みについてさらに詳細な情報は、 http:// www.datametrics.com/tech/unix/uxhistry/brf-hist.htm や http:// perso.wanadoo.fr/levenez/unix にあります。 BSD の歩みについてのさらに詳 しい情報は、 ftp://ftp.freebsd.org/pub/FreeBSD/FreeBSD-current/src/ share/misc/bsd-family-tree にあります。 少し前になりますが、短いのですが興味深い文書に John Kirch's paper ``Microsoft Windows NT Server 4.0 versus UNIX'' があります。これは Unix ライクなシステムを使うことについて、論争を引き起こしました。【訳註:日 本語訳は、Microsoft Windows NT Server 4.0 と UNIX との比較 にあり ます】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1.2. Free Software Foundation 1984 年に Richard Stallman 氏の Free Software Foundation(FSF)はフリーな Unix オペレーティングシステムを作り上げるために GNU プロジェクトを立ち 上げました。 Stallman 氏によればフリーとは自由に利用ができ、読むことが でき、修正も可能で、再配布もできることを意味します。 FSF は膨大な数の役 に立つ OS の構成要素を開発しました。その中には C コンパイラ (gcc)や素晴 らしいテキスト・エディタ(emacs)他多数の基本的なツール類があります。しか し、1990 年に FSF はオペレーティングシステムのカーネル開発で問題にぶち 当たりました[FSF 1998]。それはカーネルなしには残りのソフトウェアが動作 しないという問題です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1.3. Linux 1991 年に Linus Torvalds 氏はオペレーティングシステムのカーネルを開発し はじめ、それに「Linux」という名前をつけました[Torvalds 1999]。このカー ネルには FSF の成果物とその他の部分(BSD からいくつかと MIT の X Window System)から構成され、自由に修正可能でかつ実践的なオペレーティングシステ ムとなりました。この文書はカーネル自身を指す場合に「Linux カーネル」と し、全体を組み合わせたものを「Linux」とします。よく使う「GNU/Linux」と いう言葉は、この組み合わせを表す言葉と同じ意味で用いる場合が大半です。 Linux コミュニティでは、さまざまな組織がそれぞれ役に立つツールを組み合 わせています。それぞれの組み合わせは、「ディストリビューション」と呼ば れ、ディストリビューションを開発する組織を「ディストリビュータ」と呼ん でいます。よく知られたディストリビューションには、Red Hat や Mandrake、 SuSE、Caldera、 Corel、Debian があります。ディストリビューション間に違 いはありますが、同じコアを使ってディストリビューションを構築しています 。コアとは Linux カーネルと GNU glibc ライブラリを指します。両ソフトウ ェアとも「copyleft」スタイルのライセンスになっていて、誰もがこのコア部 分の変更を利用できなければいけないことになっています。Linux ディストリ ビューション間に存在するこの強制力は、BSD と AT&T から派生した Unix シ ステムの間には存在していません。この文書では特定の Linux ディストリビュ ーションをターゲットにはしません。 Linux について論ずる時には、前提とし て Linux カーネルのバージョン 2.2 以上で、C ライブラリがバージョン 2.1 以上とします。現状のメジャーな Linux ディストリビューションはすべてこの 前提を満たしています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1.4. オープンソースとフリーソフトウェア ソフトウェアを自由に共有することに対する関心が高まるにつれて、それを定 義し、説明することが必要不可欠になってきました。「オープンソース・ソフ トウェア」という広く利用されている用語は[OSI 1999] でさらに詳しく定義し てあります。 Eric Raymond[1997, 1998]は独創的な論文で、オープンソース・ ソフトウェアにおけるさまざまな開発プロセスについて説明しています。もう 1 つ広く使われている用語に「フリーソフトウェア」があり、ここで言う「フ リー」とは「権利としての自由」を意味します。この例としてよく出されるの は「言論の自由」であって「ただ酒」ではない、です。【訳註:free には、「 自由」と「無料」という 2 つの意味があります】。どちらの用語も完璧ではあ りません。実行形式を無償で配布できたとしても、ソースコードを見られなか ったり、修正できなかったり、再配布できなかったりしたものは「フリーソフ トウェア」とは認めないのが通例です。逆に「オープンソース」という用語は ソースコードは見られるが、利用や修正、再配布に制限があるソフトウェアを 意味する(非難する)場合に使われることがあります。さらに詳しい定義につい ては Open Source Definition を見て ください。この言葉を使う動機に違いがでる場合があります。「フリーソフト ウェア」という言葉が好きな人は、「権利としての自由」が必要であることを 強調することを好みます。一方で、他の動機(たとえば信頼性が高いこと)を持 っていたり、それ程強硬に主張したいわけではなかったりする人が使っている 場合もあります。フリーソフトウェアについての定義や目的については http:/ /www.fsf.org を見てください。 オープンソース・ソフトウェアやフリーソフトについての主張の数々に興味が あるなら、http://www.opensource.org や http://www.fsf.org をぜひ見てく ださい。その他にも Miller[1995]のようにオープンソース・ソフトウェアやフ リーソフトについて調査した文書があります。その中でオープンソースは、企 業が所有しているソフトウェアよりもずば抜けて信頼性がある、となっていま す(ソフトウェアに対してランダムな入力を行い、どれだけクラッシュに耐えう るのか、独自に計測しています)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1.5. Linux と Unix を比較する この文書では、「Unix ライクな」という言葉を、あえて Unix に似せたシステ ムを指すために使っています。「Unix ライクな」という言葉は、メジャーな Unix すべてと Linux ディストリビューションを指しています。「Unix」とい う言葉を単に「Unix ライクな」と同じ意味で使っている人が多いことも忘れな いでください。そもそも「Unix」は AT&T が開発した製品を意味します。今日 では Open Group が Unix の商標を所有していています。そこでは Unix を「 世界でただ 1 つの UNIX 規格」と定義しています。 Linux は Unix のソースコードを受け継いでいませんが、インタフェースはあ えて Unix に似せています。そのため、Unix の講義で学んだことはセキュリテ ィの知識を含めてほとんどどちらのシステムにも当てはまります。この文書の 大半の情報はどんな Unix ライクなシステムにも当てはまります。 Linux を使 うとメリットが出る場合には、あえて Linux に特化した情報を追加しています 。 Unix ライクなシステムはいろいろとセキュリティの仕組みを共有していますが 、微妙に違いがあるので、すべてのシステムでその仕組みがすべて利用できる わけではありません。プロセスに対するユーザやグループ ID(uid と gid)とフ ァイルシステムに対する読み書き、実行権(ユーザやグループ、その他)はすべ てのシステムで利用できます。 Thompson[1974]と Bach[1986]には Unix シス テム一般についての情報があり、基本的なセキュリティの仕組みについても触 れています。 Chapter 3 では Unix と Linux のセキュリティ機能で鍵となる ところを要約します。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.2. セキュリティの原則 セキュリティの原則として、当たり前に理解しておかなければならないことが たくさんあります。 Information Assurance Technical Framework(IATF)[NSA 2000]は、広くセキュリティ情報を得るのに適したところです。 NIST は「原則 や手法として広く認められている」ハイレベルな場所と見られています。また コンピュータのセキュリティを網羅しているテキストとして、[Pfleeger 1997] 等があります。ここで、セキュリティの原則についていくつか要約してみます 。 コンピュータセキュリティは全体で 3 つの目標があります。 ・ 機密を保持する(秘密を保持するともいいます)。コンピュータシステム資 産は認証を受けたメンバーだけがアクセスできることを意味します。 ・ 完全な状態を保つ。認証過程を経て許可を受け、権限を持ったメンバーだ けが資産を変更できることを意味します。 ・ 利用できること。権限を持ったメンバーが、資産へ適時(システムが必要と する要件によって決まる)アクセスできます。この目標が達成できない場合 は、サービス拒否という状態になります。 さらに目標を追加する場合もありますし、追加した目標をこの 3 つの目標の特 殊なケースとしてまとめてしまうこともあります。たとえば、拒否しないこと を独立した目標としている場合があります。これは、送り手側がメッセージを 送ったこと、受け手側がメッセージを受け取ったこと、またはその両方を「証 明」する能力のことです。たとえ、送り手側または受け手側が後でそれを否定 したくなったとしてもです。プライバシーを機密保持とは区別して扱う場合も あります。データではなく、ユーザ(たとえばユーザの身元)の機密保持を守る ことと定義する場合もあります。目標は、識別と認証を必要としている場合が よくあり、時には独立した目標として記載してある場合もあります。監査(評価 とも言います)はセキュリティの目標として好ましいとされています。同様に「 アクセス制御」や「本人であることの認証」は別物として記載してある場合が あります。どのケースであっても、プログラム全体に渡って、セキュリティの 目標と一致させることが重要です。目標をどのようにまとめようと、その目標 に合致することが重要なのです。 時にはこれらの目標が、既知の脅威に対する対抗手段である場合や法律で制定 してある場合もあります。たとえば、米国の銀行や金融機関に対して、「 Gramm-Leach-Bliley」(GLB)法というプライバシー関連の新しい法律ができまし た。この法律では、共有される個人情報を開示すること及びそれらを安全にす ることを必須とし、第三者との間で共有される個人情報の開示を要求し、さら に、それらの機関に対して顧客がデータ共有を止めさせる機会を与えるよう指 導しています [Jones 2000]。 セキュリティとシステムやソフトウェア上のその他の技術方針とが相反する場 合があります。セキュリティが「簡単に使う」ことを妨げる場合があるからで す。たとえば安全な設定を施すには、動作はするものの安全ではない設定より も手間をかける必要があるかもしれません。この相反する点を解決できる場合 は多々あります。たとえば問題点をよく考えることで、簡単に利用ができてか つ安全なシステムを構築できる場合がよくあります。セキュリティと抽象化(情 報の隠蔽)にも相反する点があります。たとえば高度なライブラリ・ルーチンは 、安全に実装してあろうがなかろうが、仕様からは何もわかりません。つまり アプリケーションに安全が求められる場合、確信がもてないなら、自分自身で 実装しなければいけません。つまりそのライブラリを修正しなければいけない ということです。あなたが不適切なライブラリ・ルーチンを選択すると、迷惑 を被るのはユーザなのです。 「徹底的に防御する」という原則は、セキュリティ上優れています。たくさん の防御機構(階層)を適した場所に配置し、攻撃に成功するには複数の機構を攻 撃側が破らなければならないように設計します。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.3. なぜプログラマは危ないコードを書いてしまうのか プログラマの多くは、危ないコードを書こうとしているわけではありませんが 、そうなってしまうのです。理由は山ほどあります。Bugtraq で Aleph One が 理由を集めて要約しています (1998 年 12 月 17 日に投稿されました)。 ・ ほとんどの教育機関には、コンピュータのセキュリティを扱うカリキュラ ムがありません。カリキュラムがあったとしても、どのようにして安全な コードを書くのかは論じられていないのが普通です。カリキュラムの多く では、暗号法やプロトコルといった特定の分野だけしか学習できません。 それらは確かに重要ですが、バッファオーバーフローや文字列のフォーマ ット、入力のチェックといった現実の世界で広く問題となっている点を論 じるのを怠っています。これは最も重大な問題点の 1 つだと私は考えてい ます。大学を卒業したプログラマでさえ、安全なプログラムをどのように 書いてよいのかについてまったく無知なのです。にもかかわらず、安全が 必要となるプログラムを書くのでさえ、そのような人々が書いたプログラ ムに頼らざるえません。 ・ プログラミングの書籍やクラスでは、安全で確実なプログラミング技術を 習得できません。実際最近になるまで、安全にプログラムする方法につい ての書籍はまったくありませんでした(この文書は数少ないものの内の 1 つです)。 ・ 誰も正式な検証法を使っていません。 ・ C 言語は安全な言語ではありません。標準 C ライブラリで用意してある文 字列関係の関数も安全とはいえません。この点はとりわけ重大です。とい うのも C 言語は非常に広範に利用されていて、 C 言語を「深く考えず」 に利用すると、危険なセキュリティホールを黙認することになります。 ・ プログラマは「複数ユーザ」の扱いを考慮しません。 ・ プログラマは人間で、人間は不精です。つまり、プログラマは安全なアプ ローチよりも「安直な」アプローチを取りがちです。つまり動作してしま えば、後から修正することはほとんどありません。 ・ たいていのプログラマは優れているとはまったくいえません。 ・ たいていのプログラマはセキュリティ関連の人間ではなく、攻撃側の考え にまで思慮が至りません。 ・ セキュリティに関っている人間は、たいていプログラマではありません。 これは Bugtraq への投稿者の何人かが主張していることで、真実であるか どうかははっきりしていません。 ・ コンピュータのセキュリティ・モデルの大半はひどい代物です。 ・ ソフトウェアの中には、既に「いかれてしまっている」のに使い続けられ ているものがたくさんあります。このソフトウェアを修正する(セキュリテ ィ上の問題を取り除き、より厳しいセキュリティ・ポリシの元で動かすよ うにする)のは困難です。 ・ 消費者はセキュリティに無関心です。 (個人的には、消費者がセキュリテ ィに関心を持ちはじめることを望んでいます。いつもやられているコンピ ュータ・システムは、役に立たないどころか、使い勝手も良くありません 。また消費者の多くは、問題があることにさえ気づいていないばかりか、 状況が好転していないことすら知りません)。 ・ セキュリティを確保するには、余計な開発時間が必要になります。 ・ セキュリティを確保するには、テストの面でも手間が増えます(仮想敵対チ ーム等)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.4. オープンソースはセキュリティに効果があるのか セキュリティを実践している人間によって、オープンソースがセキュリティに 与える影響について、数多くの議論がなされてきました。主な論点の 1 つに、 オープンソースはソースコードを公開しているので、誰でもつまり攻撃側と防 御側のどちらもがソースコードを調べられるというものがあります。この状況 が決定的な影響力を持つことに、理性的な人びとは同意していません。 ここで、このトピックについて調査してきた方々を何人か紹介します。 Bruce Schneier 氏は、「賢明な技術者なら、セキュリティに関連した事項はすべてオ ープンソースのコードに求めた方が良い」[Schneier 1999]としています。また オープンソースなソフトウェアを安全にするに必須の条件をいくつかを論じて います。 Advanced Encryption Standard (AES)の暗号化アルゴリズムを勝ち取 った開発者である Vincent Rijmen 氏は、セキュリティ上の脆弱性を簡単に見 つけ出し、それを修正するのには Linux のオープンソースの特性がこの上ない 手段であると考えています。「より多くの人々が見るということ以上に重要な のは、このモデルにおいて、より明快なコードを書くこと、標準を厳守するこ とをみんなに強制している点です。これはセキュリティ・レビューをスムーズ に繰り返し行うことに他なりません」 [Rijmen 2000] としています。 Elias Levy (Aleph1) 氏は彼の論文である "Is Open Source Really More Secure than Closed?" でオープンソ ースのソフトウェアを安全にする上での問題点を論じています。要約してみる と下記のようになります。 つまり、セキュリティ上の脆弱性が生じた時に、オープンソースとそうで ないソフトウェアは大差がないのでしょうか。それは違います。オープン ソースのソフトウェアはそうでないソフトウェアよりも、もっと安全にな る見込みが確かにあります。しかし勘違いしないで欲しいのは、単にオー プンソースだからといってセキュリティが保証されるわけでは無いという ことです。 John Viega 氏の論文である "The Myth of Open Source Security" はこの点を論じ ています。要約してみると下記のようになります。 オープンソースのソフトウェア・プロジェクトは、そうでないプロジェク トのソフトウェアよりも安全になる力を秘めています。しかしオープンソ ースのプログラムが安全になるよりどころである、ソースコードが利用で きること、たくさんのユーザがセキュリティホールを見つけてそれを修正 できるという事実は、人をだまして安心させることにもなり得ます。 Michael H. Warfield's "Musings on open source security" はさらに 確信を持って、オープンソースがセキュリティに対して影響力を持つことを主 張しています。 Fred Schneider 氏はオープンソースはセキュリティに役に立 つとは考えておらず、「多くの目が(オープンな)ソースに注がれることで、シ ステムのセキュリティを危うくしてしまうバグを発見できると信ずるに足る理 由はありません」とし、また「コードに含まれているバグが、攻撃手法として 抜きんでているわけではありません」 [Schneider 2000]とも主張しています。 またオープンソースは、プログラム構築工程の管理を考慮していない、とも主 張していますが、実際には管理は存在しています。オープンソースのメジャー なプログラムすべては、「責任者」の面目にかけて作成した公式バージョンが いくつか存在します。 Peter G. Neumann 氏は「オープン・ボックス」ソフト ウェア(ソースコードがある状況下でのみで恐らく利用可能)を論じていて、そ の中で「オープン・ボックスであるソフトウェアはシステムのセキュリティを 本当に改善するのだろうか?自然とそうなるわけではない。ただ可能性は無視 できない」[Neumann 2000]としています。 TruSecure Corporation という Red Hat(オープンソースで企業活動している)が資金提供をしている企業は、オープ ンソースがなぜセキュリティに有効であると思われているのかを書籍にまとめ ました[TruSecure 2001]。 Natalie Walker Whitlock's IBM DeveloperWorks article では賛否両論を載せています。【訳 註:Natalie Walker Whitlock's IBM DeveloperWorks article の日本語訳は、 http://www-6.ibm.com/jp/developerworks/linux/010615/j_l-oss.html にあり ます】。 Brian Witten 氏と Carl Landwehr 氏、Micahel Caloyannides 氏 [Witten 2001] は IEEE のソフトウェア関連論文でソースコードが利用できる と、システムのセキュリティに好都合に物事が運ぶはず、と控えめに結論づけ ています。彼らによると、 「私たちは、この議論からさらに 4 つの結論を引き出せます。第一に、ソ ースコードにアクセスできれば、ユーザがシステムのセキュリティを改善 できるようになります。そのような才能と人材があるならばですが。第二 に、限られたテストでは一部のケースしか指摘できませんが、オープンソ ースのライフサイクルをもってすれば、悪意の無い欠点に対して、より脆 弱さが少ないシステムを構築できます。第三に、3 つのオペレーティング システムを数年間調査したところ、12 か月に渡ってパッチが無い状態で既 知の脆弱性をさらした日数が、2 つの商用システムよりも、残り 1 つのオ ープンシステムの方が短かかった、という結果になりました。最後になり ますが、行き詰まっているのはオープンではない商用システムの開発モデ ルの方です。システムとは手間をかけて維持、サポートして行くことでよ り安全になるということ、手間をかけないシステムは安全ではないが利益 は上がる、という相反する問題であるからです。結論は出ているには出て いますが、この大切な点の議論はまだ中途半端で、消費者に提供されるセ キュリティを反映できる基準が切に望まれています」 注意して欲しいのは、時として脆弱性には、その存在を知られていないゆえに やられない場合があることです。つまりそのようなシステムは「実質的には安 全」なのです。理屈上これは真実ですが、問題は誰かがその脆弱性を見つけ出 したなら、脆弱性の修正に貢献せずに悪用するかもしれないという点にありま す。脆弱性が知られていないとしても、実際にその脆弱性がどこかに行ってし まうわけではありません。ただ、その脆弱性が時限爆弾のようにいつ悪用され るか知りようがないだけです。発見した脆弱性を誰かが悪用するという問題は 、システムがオープンソースであろうがなかろうが本来関係ありません。ソー スコードの無いシステムは、より安全であるという主張がなされてきました。 攻撃側にとって情報が少ないため、脆弱性を見つけにくい、というのがその理 由です。それに相対するものとして、攻撃側はほとんどソースコードを必要と しておらず、ソースコードを利用したいと思う時には逆アセンブルしてソース コードを再生成してしまう、という意見があります。 Flake [2001]では、オー プンでないコードに対してセキュリティ上の脆弱性をどのように調べているか を論じています(たとえば、逆アセンブラを使って)。かたや防御側は、ソース コードが無ければ問題を探しようがありません。防御側はソースコードが無い と、攻撃側と比べて不利な立場になります。 脆弱性に対しての警告を発したり、脆弱性について議論したりしない方が良い 、という主張がなされる時もあります。この説は理屈上はもっともらしいので すが、問題は攻撃側が既にありとあらゆる経路を通じて、脆弱性についての情 報を流してしまっているという点です。つまり、そのようなアプローチでは防 御側が脆弱なままで、攻撃側をまったく抑え込めません。これまで企業は、脆 弱性があからさまになるのを必死に隠し通そうとしてきました。しかし実績を 見てみると、おおかたの企業はユーザに広く知れ渡るまで脆弱性を修正しませ んでした(脆弱性を修正したと主張できたのにもかからわず)。これは「全面開 示」が必要であることの論拠になっています。 Gartner グループは CNET.com での記事「Commentary: Hype is the real issue - Tech News」で率直にコメ ントしています。 Microsoft の security response center のマネージャである Scott Culp 氏は、情報について長年言い争われていることに対し、オウムのようにお 決まりの文句を並べています。情報の配布についての道義的な議論は何度 も繰り返され、既にお馴染みになっています。たとえば、数世紀前に教会 はコペルニクスとガリレオの天動説を弾圧しようしましたが...。 Culp 氏 は Microsoft の製品で最近続発している脆弱性について「情報セキュリテ ィの専門家」を非難しようとしていますが、これは単に不誠実なだけです 。製品を製造した企業への批判を反らそうとする試みを象徴しているとも 言えますが...。関係者すべてが本当に努力すべきことは、改善のプロセス を途切れなく行うことです。より広範に脆弱性が知られれば、より速やか に修正ができます。 オープンソースのプログラムは、単独の企業が管理を強制していないため、誰 かがトロイの木馬や悪意あるコードを潜り込ませることができる、という主張 が時としてなされます。確かにトロイの木馬はオープンソースのコードに入れ 込むことは可能です。しかし同じように商用のコードにも潜り込ませられます 。従業員の中で、不満を抱えていたり、競争相手から賄賂を受けていたりして いる者が、悪意あるコードを潜り込ませるかもしれません。また組織の多くで は、オープンソースであるプログラムのように、問題を発見できそうにありま せん。なにしろ組織外の人間は、誰もソースコードをレビューできないわけで すし、社内でコードをレビューしている企業は、ごく少数に過ぎないからです (レビューを行っていたとしても、レビューしたコードが実際に使用されるとい う保証は、ほとんどありません)。オープンソースで無い企業を後で訴えられる 、という考えもほとんど根拠がないことに注意してください。ライセンスのほ とんどすべては、押し並べて保証というものを放棄しており、裁判所は普通、 ソフトウェア開発会社に責任を負わせません。 Borland の InterBase サーバの件は、この点で興味深いケースです。 1992 か ら 1994 年という長い間、Borland は故意に「バックドア」を「InterBase」と いうデータベース・サーバにしかけていました。このバックドアは、ローカル 、リモート両ユーザがあらゆるデータベース・オブジェクトの操作ができ、任 意のプログラムをインストールできてしまい、場合によっては「root」として そのマシンを制御できてしまうというものでした。この脆弱性は少なくとも 6 年もの間、製品に含まれたままでした。誰もこの製品をレビューできず、 Borland はその脆弱性を取り除くつもりもありませんでした。 Borland は 2000 年 7 月にソースコードを公開しました。「Firebird」プロジェクトがそ のソースコードとともに立ち上がり、2000 年 12 月に InterBase のこの重大 なセキュリティ上の問題が露見しました。 2001 年 1 月に CERT はこのバック ドアの存在を CERT advisory CA-2001-01 として公表しました。あきれたことに、そのバックドアはプ ログラムの ASCII ダンプ(クラッカーがよく使う手)をちょっと眺めただけで発 見できるようなものでした。この問題はオープンソースの開発者がコードをレ ビューすることで発見され、すみやかにパッチが当てられました。パスワード を知られなければプログラムは安全なままで、ソースを公開するとプログラム が安全でなくなると主張されるかもしれません。私はそれは無意味だと考えま す。ASCII ダンプは平凡で良く知られた攻撃手段の 1 つです。攻撃者すべてが 脆弱性を突然公開する衝動に駆られるわけではありませんし、実際のところ、 その脆弱性が何度にも渡って悪用されなかった、という確かな証拠はありませ ん。はっきりしていることは、ソースが公開された後、ソースコードが何回も レビューされ、脆弱性が見つかって修正された、ということだけです。この状 況をまとめると、オリジナルのコードに脆弱性があり、オープンソースになる やいなや簡単にその脆弱性が発見され、最終的には修正されたということです 。 ソースコードをオープンにする利点は、攻撃を受けるソフトウェアが増えない ということではなく、脆弱性を走査し評価する機会が増えるということです。 脆弱性を走査し評価するには、設定済みのシステムで意図的に脆弱性を探し出 します。最近になって Network Computing evaluation は、最高の走査ツール (数ある脆弱性の中でも最も悪質な脆弱性を発見した)は、オープンソースの走 査ツールである Nessus としました[Forristal 2001]。 いったい結論は何なのでしょう。個人的には、プログラムというものがまずオ ープンソースとして作られると、最初はユーザにとって安全性が低くても(脆弱 性があらわになっても)、時を経るにしたがって(数年程度)、オープンでないプ ログラムよりもさらに安全になる可能性があると考えています。プログラムを ただオープンソースにするだけでは、すぐには安全になりませんし、オープン ソースなプログラムにすることで、安全になる保証もありません。 ・ まず実際にコードをレビューしなければいけません。これは議論において 重要な点の 1 つです。現実的にオープンソースなプロジェクトであれば、 コードはレビューされるのでしょうか。レビューを受ける回数が少なくな る要因はいろいろとあります。ニッチでほとんど利用されない製品(レビュ ーアの存在が期待できない)や開発者がほとんどいない場合、まれにしか使 われないコンピュータ言語がそれにあたります。開発者が一人で協力者が いないプログラムは、間違いなくこの手のレビューを受けられません。そ の一方でプログラムの中には、中心となる作者が存在しかつ、ちょくちょ くコードを調べたり、他の人間が行ったレビュー(少なくとも貢献にはなっ ている)を提案したりするメンバーがたくさん関っている場合もあります。 一般的にレビューアがたくさんいればいるほど、誰かが欠点を見つけ出す 可能性がより高くなります。これは「たくさんの眼」理論の基本です。 オープンソースであること自体が、レビューを受ける見込みを著しく減ら す要因の 1 つになるわけではありません。ベンダーの中には「開示されて いるソース(disclosed source)」(「ソースが存在する」 (source available)とも言う)プログラムをオープンソースだとポーズをとるところ があります。しかしそのプログラムの所有者はあいかわらず広範囲に独占 的な権利を有しているので、「無償で」所有者のために働こうという意欲 がある人はほとんどいないでしょう。風変わりで変則的な権利形態をとる (MPL のような)オープンソースのライセンスでさえ、問題を抱えています 。結局は、自分の成果に対して別の誰かが権利を持っているのであれば、 ボランティアで参加する可能性は低くなるということです(Bruce Perens 氏はこの点について、「一体誰が好んでただ働きで雇われの身になるんだ い?」と言っています)。やる気が旺盛なレビューアは、プログラムも修正 したがります。このやる気を削ぐようなライセンスでは、たくさんの「眼 」を失うことになります。 Elias Levy 氏はオープンソースのセキュリテ ィに関する彼の論文の中でこの点で間違いを犯しています。彼が分析した ソフトウェア(例えば TIS の Gauntlet)は当時オープンソースではなかっ たのです。 ・ 第二に、コードを書いたり、少なくともレビューしたりする人の中に、安 全なプログラムの書き方を理解している必要があります。できれば、この 文書が役に立てばと願っています。「たくさんの眼」があっても、何を見 つけだすかを知らなければ、何にもなりません。理解している人たちがい る限りは、みんながみんな安全なプログラムの書き方を知らなくてもかま わない、という点に気をつけてください。 ・ 第三に、一度問題が見つかったなら、すみやかに修正してそれを配布しな ければいけません。オープンソースのシステムでは、速やかに問題が修正 される傾向にありますが、スムーズに配布されるとは限りません。たとえ ば、OpenBSD の開発者たちはセキュリティ上の欠陥をレビューするのに長 けています。しかし、確認した問題点をオリジナルの開発者にいつもフィ ードバックするとは限りません。つまりあるシステムのあるバージョンを 修正するのには都合が良いのですが、他のシステムは直されないままにな ってしまいます。 オープンソースのもう 1 つの長所は、問題をみつけたなら、あなたがそれをす ぐに修正できるということです。 つまり、オープンソース・ソフトウェアのセキュリティへの影響度合いは、セ キュリティ界でまだ広範囲に渡って議論している最中です。しかし、著名な専 門家の多くはより安全になる可能性が大であると考えています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.5. 安全なプログラムの種類 安全にする必要があるプログラムは、多岐に渡ります(用語については、この文 書で定義します)。一般的な種類を下記にいくつかあげます。 ・ 離れた所にあるデータを閲覧する場合に利用するアプリケーション・プロ グラム。ビューアーとして利用するプログラム(ワード・プロセッサやファ イル・フォーマットを見るためのビューアー)では、信頼できないユーザが 、離れた場所から閲覧するデータを送るように求めることがよくあります (このリクエストは Web ブラウザが自動的に実行する可能性があります)。 はっきりしていることは、信頼できないユーザの入力によって、アプリケ ーションが任意のプログラムを実行してしまってはいけない、ということ です。初期化マクロ (データを表示する時に実行する)をサポートするのは 浅はかです。サポートせざるを得ないなら、必ず安全のためのサンドボッ クスを作成してください(サンドボックスを作る作業は複雑かつ間違いを起 こしやすいので、うまくいくとは限りません。したがって、はなからマク ロをサポートするべきではないのです)。 Chapter 5 で議論するバッファ オーバーフローのような問題についても、注意が必要です。この問題は、 信頼できないユーザがビューアーを使って、任意のプログラムを強制的に 動かせるようにしてしまいます。【訳註:サンドボックス(sandbox)とは、 制限付きで保護されたメモリ領域。この領域で動くアプリケーションは、 システムにダメージを与えないように設計、動作します】 ・ システム管理者(root)が使用するアプリケーション・プログラム。そのよ うなプログラムは、システム管理者以外が制御できてしまうデータを信頼 すべきではありません。 ・ ローカルでサービスを行うサーバ(デーモンとも呼びます)。 ・ ネットワーク経由でアクセスするサーバ(ネットワーク・デーモンという場 合もあります)。 ・ Web ベースのアプリケーション(CGI スクリプトもその一部)。これらのア プリケーションは、ネットワーク経由でアクセスするサーバとしては特殊 なケースです。しかし、あまりにも普及しているので、これだけで一分野 を作る価値があります。この分野に属するプログラムは、Web サーバを経 由して間接的に実行され、フィルタされる攻撃も中にはありますが、防御 すべきでありながら、多くはなすがままになっています。 ・ アプレット(すなわち、クライアントにダウンロードされ、自動的に実行す るもの)。 Java がとりわけ有名ですが、他の言語(たとえば Python)も同 様にモバイル・コードをサポートしています。ここにセキュリティ上重要 な点がいくつかあります。それは、クライアント側でアプレットの実行機 構を実装した人が、「安全な」オペレーションをだけを確実に実行するよ うにしているか、という点とアプレット作成者が、悪意のあるホストの問 題(つまりクライアントは普通信頼できない)に対処しなければいけない点 です。悪意のあるホスト上で、アプレットを問題なく動かす研究がいくつ かありますが、正直言って、この解決方法は疑問です。斬新なテーマなの で、ここではこれ以上触れません。【訳註:モバイル・コードは、クライ アントプログラム(たとえば Web ブラウザ) が相手のシステム(たとえば Web サーバ等)からダウンロードし、自動的に実行されるプログラム全般を 指します】 ・ setuid や setgid したプログラム。これらのプログラムは、ローカルにい るユーザが実行します。実行されるやいなやそのプログラムのオーナーも しくはオーナーのグループ(もしくはその両方)の権限が与えられます。い ろいろな意味で、これらは最も安全にしにくいプログラムです。それは入 力の大部分が信頼できないユーザが制御していて、その入力自体も疑いが あるからです。 この文書は、さまざまな種類のプログラムの課題をひとまとめにして扱ってい ます。このやり方には欠点があります。それは、ここで扱う問題点には、プロ グラム全種類へ適用できないものがある点です。特に setuid や setgid した プログラムは、思いがけないさまざまな入力があり、ガイドラインには setuid や setgid したプログラムだけに当てはまるものもあります。しかし、実際は そんなに区別がはっきりしているわけではなく、あるプログラムではこの範疇 を越えるものもあります(たとえば CGI スクリプトは setuid や setgid され たり、同じような影響がある方法で設定してあったりします)。また実行形式が いくつにも分かれていて、そのそれぞれが異なる「種類」のプログラムになっ ている場合もあります。さまざまな種類のプログラムをひとまとめにして検討 する利点は、あるカテゴリにプログラムを無理に当てはめることなく、問題を 包括的に検討できる点にあります。こうして見ていくことで、安全が求められ るプログラムすべてに対して、原則が当てはまるケースが多いことがわかりま す。 この文書は、C で書いたプログラムに多少偏っています。C++ や Perl、PHP、 Python、 Ada95、Java のような他の言語についても、多少は扱っています。こ れは Unix ライクなシステムでは C が安全なプログラムを実装する言語として 最も普及しているためです(CGI は例外で、Perl や PHP、Python をよく使いま す)。また C 以外の言語の大部分は、 C のライブラリを呼び出すように作られ ています。これをもって C が安全なプログラムを作るという目的に「最良の」 言語である、ということにはなりません。ここで述べられる原則の大部分は、 使用している言語のいかんにかかわらず当てはまります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.6. 疑い深く、こだわりが強いことに価値がある 安全なプログラムを書くのに当たって一番やっかいな点は、作成者が思考回路 を切り替えなければならないところにあります。つまり、疑い深く、こだわり を強く持つ必要があることです。エラー(欠陥とかバグとも言われています)が 起こすシステムへの影響が、普通のプログラムとはまったく違うからです。 安全が必要とされていない普通のプログラムには、エラーがたくさんあります 。エラーは歓迎すべきものではありませんが、そう度々は起こらないのが普通 で、ありそうでありません。ユーザが万が一エラーに遭遇してしまっても、そ のバグを何とか避けながら、利用し続けようとするでしょう。 安全が必要なプログラムではこの状況が一変します。とあるユーザは、意図的 にバグを捜し出し、ほとんど起こりそうもない状況を引き起こします。そして 、そのような攻撃で不当な権限を得ようとします。という訳で、安全なプログ ラムを書く場合には、疑い深く、こだわりを強く持つことに価値がでるのです 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.7. このドキュメントを書いた訳は ? 「どうしてこのドキュメントを書いたのか?」と質問を受けるのですが、私の 答えは下記の通りです。ここ数年来、Linux や Unix のデベロッパーは同じよ うなセキュリティ上の落し穴に何度もはまりこんでいるように思えます。監査 する側は、問題をとらえるのが早いとは言えません。しかしバグが最初からコ ードに入っていなかったなら、もっと良かったはずです。既知の落し穴にはま らない方法について、ここさえ見れば、という共通の所がないところに問題の 一端があると考えています。情報は公開されているのですが、その情報を捜し 出すのが困難だったり、古くなっていたり、不十分だったり、別の問題を含ん でいたりします。またそのような情報の大部分には Linux が広く利用されてい るにもかかわらず、 Linux に焦点を当てた議論がまったくありません。このよ うな背景から、ソフトウェアの開発者が過去の過ちを今後繰り返さず、システ ムがより安全となることを願って、このドキュメントを書きました。この点に ついてさらに知りたければ、http://www.linuxsecurity.com/feature_stories/ feature_story-6.html を参照してください。 これと関連した質問に「他のドキュメントを参照するのに留めずに、どうして 自分自身でドキュメントを書いたのか?」というものもあります。答えはいく つかあります。 ・ 情報の多くは、あちこちに散在しています。重要な情報を体系的なドキュ メントにまとめれば、利用しやすくなります。 ・ 情報の中にはプログラマ向けではなく、システム管理者やユーザ向けの情 報もあります。 ・ 入手可能な情報は、システム間で共通な要素(Unix ライクなシステムすべ てで動作する)に重点をおいている場合が多く、Linux についてはまったく 論じられていません。移植性の点からすれば、Linux 固有の機能に触れる のを避けるのが確かに賢明です。しかし Linux に固有の機能を使えば、セ キュリティが確実に向上する場合があります。 Linux 以外への移植性が要 求されても、Linux が動いていれば固有の機能を使いたくなるかもしれま せん。そして Linux を中心として、Linux を対象としている人々に役に立 つ情報への参照を入れることがあります。その情報が他には必ずしも役立 つとはいえないとしてもです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.8. 設計と実装の指針についての情報源 安全なプログラムを書く方法(あるいは、既存のプログラムのセキュリティ上の 問題点をどのように発見するのか)について、役立つドキュメントがいくつかあ ります。またそれらのドキュメントは、このドキュメントでガイドラインとし てこれから明確にしていく項目の根拠にもなっています。 汎用的なサーバや setuid もしくは setgid したプログラムについては、役に 立つドキュメントがたくさんあります(ドキュメントの中には直接見つけるのが 困難なものがあります)。 Matt Bishop[1996, 1997] は、このトピックに関してたいへん素晴らしいドキ ュメントを作成したり、発表を行ったりしています。その上彼の Web サイト http://olympus.cs.ucdavis.edu/~bishop/secprog.html でこのトピックを専門 に扱っています。 AUSCERT はプログラミングにあたってのチェックリスト [AUSCERT 1996] を公開しています。これは Garfinkel、 Spafford 両氏の著作 [Garfinkel 1996] の 23 章で論じている、安全な suid したネットワーク関連のプログラ ム書法を下敷きにしています。 Galvin [1998a] は、安全なプログラムを開発する工程 やチェックリストについて分かりやすく説明しています。最近になってチェッ クリストが更新され、 Galvin [1998b] で見られます。 Sitaker [1999] には 、「Linux セキュリティ監査」チームが検討している問題について、その一覧 があります。 Shostack [1999] ではセキュリティに気を配る必要があるコードをレビューするの に役立つチェックリストを定義しています。 NCSA [NCSA] では、安全な プログラムのための簡潔かつ役に立つガイドラインを提供しています。その他 で情報源として役に立つのは、 Secure Unix Programming FAQ [Al-Herbish 1999] や Security-Audit's Frequently Asked Questions [Graham 1999] 、 Ranum [1998] があります。アドバイスの中には 注意しなければならないものがあります。例えば BSD の man の setuid(7) で は [Unknown] 、 access(3) の使用を推奨していますが、その使用にともなって生じる競合状態の危険さを 考慮していません。 Wood[1985]には、役に立つものの、古くなってしまったア ドバイスが「Security for Programmers」の章にあります。 Bellovin [1994] には役に立つガイドラインや ftpd の実装をよりシンプルで安全に組み直すといったやり方の具体例がいくつ かあります。 FreeBSD は FreeBSD [1999] というガイドラインを用意しています。 [Quintero 1999] は、そもそも GNOME のプログラミング・ガイドラインと関連があ りますが、セクションの 1 つでセキュリティについて検討しています。 [Venema 1996] は、安全なプロ グラムを組む時にありがちなエラー(ありきたりなので推測可能なパスワードや 悪意あるデータによる汚染、ユーザがアクセスできるデータに含まれてしまっ ている機密情報、他のプログラムへの依存)について詳しく (例を挙げて)論じ ています。 [Sibert 1996] は 、悪意あるデータが引き起こす脅威について説明しています。 Web のインタフェースである Common Gateway Interface(CGI)は、プログラマ 向けドキュメントとして、セキュリティのガイドラインがたくさん用意してあ ります。 Van Biesbrouck [1996] , Gundavaram [unknown] , [Garfinkle 1997] Kim [1996] , Phillips [1995] , Stein [1999] , [Peteanu 2000] , and [Advosys 2000] . ある特定の言語について触れたドキュメントはたくさんあります。このドキュ メントでは、言語に固有な点に触れたセクションでさらに論じます。たとえば 、Perl の配布物の中には、 perlsec(1) というセクションがあり、Perl をより安全に 使う方法について論じています。 http://www.cs.princeton.edu/sip にある Secure Internet Programming というサイトは、コンピュータのセキュリティ 全般について扱っていますが、Java や ActiveX、JavaScript といったモバイ ル・コードの仕組みに焦点を当てています。 Ed Felten 氏(このサイトの中心 人物の 1 人)は Java を安全にする書籍を共著しています。 ([McGraw 1999] )。この点については Section 9.6 で扱います 。 Sun が出しているコードを安全にするためのガイドラインには、主に Java や C について触れたものがいくつかあります。 http://java.sun.com/ security/seccodeguide.html で利用できます。 Yoder[1998]には、アプリケーションのセキュリティに取り組む際に利用できる パタンがいろいろあります。ガイドラインとしては具体性に欠けますが、日常 よく使うプログラミング・パタンとして役に立つと思います。 Schmoo グルー プは、安全なコードを書く方法についての情報リンクを Web サイトに載せ続け ています。http://www.shmoo.com/securecode。 別の側面から問題を論じているドキュメントも、たくさん存在します(たとえば 「システムをクラックするには」)。一例として McClure[1999]があげられます し、インターネットという世界で見れば、さらに莫大な資料がころがっていま す。また、あるコンピュータ・アーキテクチャを攻撃するにはどのように開発 すればよいか等についても、より広範囲なドキュメント(たとえば [LSD 2001] のような) が存在しています。 Honeynet プロジェクトは、実際にどのように 攻撃を行っているのかについて、情報 (統計を含め)を集めています。 http:// project.honeynet.org を見れば詳細な情報が得られます。 既存のプログラムにおいて、脆弱性が既に確認されている情報も多量に出回っ ています。この情報は、「そうしないようにする」という点では役に立ちます 。しかし、たくさんの具体的な例から、一般的に利用できるガイドラインを見 つけ出すのはかなり大変です。セキュリティについて議論するメーリングリス トがあります。最も有名なものの 1 つに Bugtraq があります。このメーリングリストは脆弱性の一 覧を作成することに熱心に取り組んでいます。 CERT Coordination Center (CERT/CC)は、代表的な機関の 1 つで、インターネット関連のセキュリティ上 の問題を報告しています。 CERT/CC はちょくちょく勧告を発行し、深刻なセキ ュリティ上の問題やその影響度合いを解説し、パッチや善後策をどうやって得 たらよいのかを説明しています。詳しい情報は、 http://www.cert.org を見て ください。ただし注意して欲しいのは、もともと CERT は小型コンピュータ緊 急対応チームであって、公式に「CERT」がセキュリティについての代表機関と なっているわけではない点です。米国エネルギー省の機関である Computer Incident Advisory Capability (CIAC) も脆弱 性について報告をあげています。それぞれのグループは同じような脆弱性を報 告していますが、ばらばらな呼び方をしています。この問題を解決するために 、MITRE は、「あきらかな脆弱さと起こる可能性がある脆弱さの共通化」 (Common Vulnerabilities and Exposures(CVE))の一覧を作成しています。この 一覧では、共通で一意に決まる識別子(name)を作って、一般的に広く知られて いる脆弱性と誰かが発見したセキュリティ上の問題を載せています。 http:// www.cve.mitre.org を見てください。 NIST の ICAT はコンピュータの脆弱性 を検索可能な形でまとめたものです。CVE の脆弱性のカテゴリにもとづいてい るので、後で検索や比較が可能になっています。 http://csrc.nist.gov/icat を見てください。【訳註:MITRE と CVE の詳細は、 about MITRE と about CVE を参照 してください】 このドキュメントは、私が最も有益かつ重要だと考えたガイドラインをまとめ たものです。優秀なプログラマがこれを読んだだけで、かなり上手に安全なプ ログラムを実装できるよう、下地を作ることを目標にしています。この目標を 単独でカバーできるドキュメントにはお目にかかったことはありませんが、こ の取り組みは意義があると信じています。方針はバランスを取ることです。「 考えられるだけのガイドラインをリストアップする」(エンドレスな作業でいつ までたっても形にならない)こと、「簡潔な」リストをいろいろあげてオンライ ンで利用できれば、有益かつ簡潔にはなるものの、重大な問題が省略されてし まうことが多く、この両者のバランスが大切です。はっきりしない場合は、手 引きを記載してあります。そういう場合は、誰もが「これさえ見ればすべて OK 」的なドキュメントを読んで、その情報を活用できるようになっている方が、 より効果的だと考えるからです。このドキュメントの構成(一覧はすべて、独自 でさまざまな構成になっています)は、私自身が作成し、ガイドライン(特にケ イパビリティや fsuid のような Linux 独自のもの)の中にも、私自身が書いた ものがあります。上記にあげた関連ドキュメントをすべて読むことを強くお薦 めします。しかし、それは現実的ではないですね。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.9. その他のセキュリティ情報源 セキュリティ問題に特化した Web サイトやメーリングリストは膨大な数が存在 しています。ここでは、その他のセキュリティ情報をいくつか挙げておきます 。 ・ Securityfocus.com は、一般的なセキュ リティ関連のニュースや情報を豊富に提供しており、セキュリティ関連の メーリングリストもたくさん主催しています。参加方法やアーカイブの見 方については、Web サイトを見てください。 SecurityFocus で最も関連深 いメーリングリストには、下記のようなものがあります。 □ 上記で触れたように、「Bugtraq」メーリングリストは「コンピュータ のセキュリティ上の脆弱性についてのメーリングリストで、モデレー ターが存在しています。脆弱性が何であり、それがどのように攻撃を かけ、どうしたら防ぐことができるか、といったことについて、詳細 な報告と議論をすべて公開しています」。 □ 「secprog」メーリングリストは、モデレーターがいるメーリングリス トで、安全なソフトウェアを開発する方法論とテクニックを議論して います。私はこのメーリングリストをとても注目しています。モデレ ーターと連携して、 secprog で出た結論を取り入れ(結論に納得した 場合)、このドキュメントに反映しています。 □ vuln-dev メーリングリストは、潜在的にセキュリティホールとなるが 、まだ穴を空けられていないものを議論しています。 ・ IBM の「developerWorks: Security」は興味深い記事を集めています。 http://www.ibm.com/developer/security でさらに学習してください。【 訳註:日本語サイト があり ます】 ・ Linux 固有のセキュリティ情報を知りたいなら、 LinuxSecurity.com がよいでしょう。 Linux のコードを監 査することに興味があるなら、 Linux Security-Audit Project FAQ や Linux Kernel Auditing Project も見てみるとよいでしょう。これらは、 Linux のコードについてのセキュリティ上の問題に熱心に取り組んでいま す。 特定のシステムを安全にしようとしているなら、そのシステムのセキュリティ 関連のメーリングリストにももちろん参加してください(たとえば Microsoft や Red Hat 等)。そうすれば、セキュリティのアップデート情報を得られます 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.10. ドキュメントでの約束事 システムの man ページからの引用は、名称(番号)という形式にします。番号は 、 man のセクション番号です。「どこも参照していない」ポインタ値を NULL とします。C コンパイラは、ポインタが必要なおおかたの環境で、整数の 0 を NULL に変換しています。しかし、C の規格としては NULL が実際にすべてが 0 ビットで埋められるという実装を求めてはいません。 C と C++ は文字「\0 (ASCII の 0)」を特別扱いしています。この値をこのドキュメントでは NIL と します(NIL は通常「NUL」としますが、「NUL」と「NULL」の発音が同じため) 。関数やメソッドの名前は、文脈によっては小文字ではじめる必要があったと しても、常に大文字・小文字の使い分けは元のままにしています。「Unix ライ クな」という用語を使う時には、Unix や Linux、そして Unix と非常に似通っ た基本構造を持つその他のシステムを指します。 POSIX という言葉は使いませ ん。というのは、Windows 2000 のように POSIX を部分的に実装はしていても 、まだセキュリティモデルがまったく異なっているものがあるからです。 攻撃者のことを「アタッカー」とか「クラッカー」とか「アドバーサリ(敵対 者)」と言います。ジャーナリストは「攻撃者」のかわりに「ハッカー」という 言葉を使う場合があります。このドキュメントではこの(間違った)表現を避け ています。というのも、 Linux や Unix の開発者は、自身のことを「ハッカー 」としている場合が多いからです。この言葉は悪い意味で使われてはいません でした。つまり、Linux や Unix の開発者にとって「ハッカー」という言葉は 、今でも専門家であり、コンピュータにとりわけ熱い思いを持っている人間を 意味しているのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 3. Linux と Unix のセキュリティ機能 慎重さがあなたを保ち、英知が守って くれる 旧約聖書箴言 2 章 11 節 Linux や Unix のセキュリティ機能のガイドラインについて論じる前に、プロ グラマの立場から、その機能が何であるかを知っておいてもよいと思います。 このセクションでは、Unix ライクなシステムのほとんどで広く使われているこ れらの機能をざっと説明します。しかし Unix ライクなシステム間にはかなり ばらつきがあり、システムすべてがここで述べる機能を持っているわけではな いことに注意してください。この章では、拡張機能のいくつかと Linux に固有 の機能についても注目して行きます。Linux のディストリビューション間の違 いは、セキュリティに関するプログラミングという点から見ると、ほとんど無 いといってもよいと思います。それは、基本的には同じカーネルと C ライブラ リ(GPL ベースのライセンスは、技術革新を速やかに普及させます)。また、そ れぞれの Unix の実装間にはセキュリティ関連での違いがいくつかありますが 、ここで説明することがすべてをカバーしているわけではありません。この章 では、たとえば強制アクセス制御(mandatory access control (MAC)) の実装の ように、まだ Unix ライクなシステムの多くが実装していないものについては 議論しません。これから説明する機能が何であるか既に知っているなら、この セクションを飛ばしても結構です。 プログラミングガイドは、Linux や Unix でのセキュリティ関連の一部を軽く なぞるだけで、重要な情報は飛ばしてしまうケースが多く見られます。とりわ け「どのように使うか」ということは大まかには論じますが、利用の際に関り が出てくるセキュリティの属性については誤魔化しています。それとは逆に、 man には個々の関数について、詳細な情報が多量にあります。しかし man は個 々の関数をどのように利用するのか、という細かい説明でもって、セキュリテ ィの問題を隠してしまっている場合があります。このセクションでは、そのギ ャップを埋めるように心がけます。Linux でプログラマがよく利用しそうなセ キュリティの仕組みについて概要を説明しますが、特にセキュリティによって 生じるやっかい事に焦点を当てます。このセクションは、普通のプログラミン グ・ガイドよりもさらに突っ込んだ内容になっていて、セキュリティ関連の事 柄に焦点を絞り、さらに詳しい情報が得られるように参考文献をあげておきま す。 まず基本的なところから。 Linux や Unix は 2 つの部分から構成されていま す。それはカーネルと「ユーザ空間」です。プログラムのほとんどは、ユーザ 空間(カーネル上の)で動作しています。 Linux は「カーネル・モジュール」と いう概念をサポートしていて、動的かつ簡単にコードをカーネルに追加できる ようになっています。しかし依然としてカーネルは基本的な部分を内部に抱え ています。他のシステムの中には(たとえば HURD のような)、「マイクロカー ネル」ベースのシステムもあります。そのシステムは、機能をより限定した小 さなカーネルと従来はカーネルで実装していた低レベルの機能を「ユーザ」プ ログラムとして実装しています。 Unix ライクなシステムには、大幅に改修を行って、米国国防省が要求している MAC(B1 レベル以上)の強固なセキュリティに特別対応しているものがあります 。この版のドキュメントでは、これらのシステムやその課題は扱いません。そ のうち範囲を広げて行きたいと思っています。さらに詳細な情報のいくつかは 、他のところで利用できます。たとえば、SGI の「Trusted IRIX/B」について 詳しいのは、NSA の Final Evaluation Reports (FERs) です。 ユーザがログインすると、そのユーザ名はユーザが属している uid(ユーザ ID) や gid(グループ ID)を表す整数値に割り当てられます。 uid が 0 のユーザは 特別な権限(役割)を持っていて、「root」と言われてきました。 Unix ライク なシステム(Unix も含む)のほとんどでは、root はセキュリティチェックのほ とんどを受けることなく、システムを管理を行う場合に使用されてきました。 Unix システムの中には、gid が 0 のユーザも特別扱いになっていて、グルー プレベルでリソースに対して無制限のアクセス権を持っているものもあります [Gay 2000, 228]。この事は他のシステム(Linux のような)では当てはまりませ んが、そのようなシステムであってもグループ 0 は、基本的にすべての権利を 持っています。というのも、システム関連の特別なファイルは、グループ 0 が 所有しているケースが多いからです。セキュリティの点から見て唯一「対象」 となるもの、それがプロセスです (いろいろなことを実行している正体がプロ セスなのです)。プロセスはさまざまなデータにアクセスできます。ファイルシ ステム(FSO)であったり、 System V のプロセス間通信(IPC)であったり、ネッ トワーク・ポートであったりします。プロセスはシグナルを設定できます。そ の他セキュリティ関連のトピックとしては、quota や limit、ライブラリ、監 査、 PAM があります。この後、サブセクションで詳しく見ていきます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.1. プロセス Unix ライクなシステムでは、ユーザレベルの動作はプロセスを動かすことで実 現しています。 Unix システムの大部分は「スレッド」をプロセスとは独立し た概念としてサポートしています。スレッドはプロセス内でメモリを共有して いて、システムのスケジューラはスレッド自身をスケジューリングしています 。 Linux はこれとは異なる方法で実行しています(私は優れたやり方だと思い ます)。スレッドとプロセスは基本的に違いはありません。そのかわり Linux においては、プロセスがもう 1 つのプロセスを起こした時に、どのリソースを 共有するのかを選択できます(たとえばメモリを共有するとか)。そして Linux カーネルは、スレッドレベルで速度が最適になるように動作します。詳しい情 報は clone(2) を見てください。 Linux のカーネル開発者は「スレッド」や「 プロセス」というかわりに、「タスク」という言葉をよく使う点に注意してく ださい。しかし、対外的なドキュメントには、プロセスという言葉を使います (なので、ここではプロセスという言葉を使います)。マルチ・スレッドのアプ リケーションをプログラムする場合には、上記のような違いを隠蔽するため、 通常は標準のスレッドのどれかを使った方が適切です。こうすると移植性が高 くなるだけでなく、ライブラリが間接的なレベルの機能追加を提供できます。 これは、複数のアプリケーションレベルのスレッドを、あたかもオペレーティ ングシステムの単独のスレッドとして実行することで実現しています。こうす ることで、あるシステムの何らかのアプリケーションは、性能がいくらか向上 できます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.1.1. プロセスの属性 ここでは、Unix ライクなシステムで動くプロセスそれぞれが持っている代表的 な属性を挙げておきます。 ・ ruid、rgid ――実 uid と実 gid のことで、この権限でプロセスが動いて います。 ・ euid、egid ――実効 uid と実効 gid は権限のチェックに利用します(フ ァイルシステムを除く)。 ・ suid、sgid ――保存 uid と保存 gid は下記で論じますが、権限の「有効 化・無効化」をサポートするのに利用します。 Unix ライクなシステムの すべてがこの機能をサポートしているわけではありませんが、大半のシス テム(Linux や Solaris を含む)がサポートしています。システムがこの機 能を POSIX 標準で実装しているかを調べたいなら、sysconf(2) を使って _POSIX_SAVED_IDS が有効か確かめてください。 ・ 補助グループ――グループ(gid)の一覧で、ユーザがメンバーになっている グループの一覧です。オリジナルの version 7 Unix では、存在していま せん。プロセスは同時に複数のグループには属せず、特別なコマンドを使 ってグループを変更しなければいけません。 BSD では、それぞれのプロセ スでのグループの一覧をサポートしており、より柔軟な構成をとっていま す。またこの追加機能は現在では広く実装されています (Linux や Solaris を含む)。 ・ umask ――新しいファイルシステムの実体を作成する時に、デフォルトの アクセス制御の設定を決めるビット列です。umask(2)を見てください。 ・ スケジューリング・パラメタ――プロセスにはそれぞれ、デフォルトのス ケジューリングのポリシがあります。SCHED_OTHER は、追加でパラメタを 設定でき、 nice やプライオリティ、カウンターを設定できます。詳細は 、 sched_setscheduler(2) を見てください。 ・ limits ――プロセス単位のリソースの制限です(下記参照)。 ・ ファイルシステムのルート――プロセスから見たルート・ファイルシステ ム("/")の位置。 chroot(2)を参照してください。 ここでは、プロセスに関連してはいるものの、あまり一般的ではないものを説 明します。 ・ fsuid、fsgid ―― uid と gid はファイルシステムへのアクセス権限をチ ェックするのに使います。通常は、それぞれ euid や egid と同じです。 この属性は Linux 独自です。 ・ ケイパビリティ―― POSIX で定義しているケイパビリティ情報。プロセス についてのケイパビリティは、3 つあります。それは、実効、継承、許可 です。POSIX ケイパビリティについての詳しい情報は、下記を見てくださ い。 Linux のカーネル 2.2 バージョン以上であればこの機能をサポート しています。他の Unix ライクなシステムでもサポートしていますが、一 般的というわけではありません。 Linux において、どの属性が各々のプロセスに関連しているのかを正確に知る 必要があるなら、Linux のソースコードが最も信頼のおける情報源です。特に 、 /usr/include/linux/sched.h にある task_struct という定義は重要です。 新しいプロセスを起こすには、fork(2) システムコールを使うのが一般的です 。 BSD は vfork(2) という最適化をはかった改良版を導入しました。 vfork (2) の基本的な考えは単純で、使う必要がなければ使わないです。詳しくは、 Section 7.6 を見てください。 Linux では、固有のシステムコールである clone(2)をサポートしています。こ のシステムコールは fork(2) と同様に動作しますが、共有したいリソース (た とえば、メモリやファイル・ディスクリプタ等)を指定できます。さまざまな BSD システムでは rfork() システムコール(オリジナルは Plan9 で開発)を実 装しています。呼び出し方は異なりますが、基本的な考え方は同じです(これも 共有するものに対しての操作を強化して、プロセスを生成します)。プログラム に移植性を持たせるなら、できればこれらのシステムコールをそのまま使うべ きではありません。上記でも述べましたが、移植性を持たせるなら、それらの システムコールを用いたスレッドライブラリをもとにした方が良いでしょう。 このドキュメントは、プログラムを書くための完璧なチュートリアルではあり ません。したがって、プロセスを扱った一般的に利用できる情報については省 略しています。さらに情報として wait(2) や exit(2) といったドキュメント も利用できます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.1.2. POSIX ケイパビリティ POSIX ケイパビリティはビットで値を構成し、普通 root が持っている権限を 分割し、より特化した権限を持つ大きな組み合わせとして再構成します。 POSIX ケイパビリティは、IEEE 標準のドラフトで定義されています。したがっ て Linux 固有の機能でもありませんし、他の Unix ライクなシステムで広く採 用されてもいません。 Linux のカーネル 2.0 では POSIX ケイパビリティをサ ポートしていませんが、 2.2 ではプロセスについて、POSIX ケイパビリティを サポートしています。 Linux のドキュメント(このドキュメントも含め)の中で 、「root の権限が必要である」と書いてあった場合、「ケイパビリティが必要 である」とほぼ同じ意味になる、とケイパビリティについてのドキュメントに 述べられています。個々のケイパビリティについて知りたい場合は、ケイパビ リティに関するドキュメントを読んでください。 Linux においては、ファイルシステム上にあるファイルに対してケイパビリテ ィを適用することが最終目的です。しかし、これを書いている時点では、まだ 実装されていません。転送機能に対するケイパビリティはサポートされていま すが、デフォルトでは無効になっています。Linux のカーネル 2.2.11 ではケ イパビリティをより身近に利用しやすくする機能が加わりました。その機能は 「ケイパビリティ・バウンディング・セット(capability bounding set)」です 。ケイパビリティ・バウンディング・セットは、ケイパビリティのリストの 1 つで、システム上のどのプロセスもその管理下に入ります(さもなければ、特別 な init プロセスだけが管理します)。ケイパビリティがバウンディング・セッ トになければ、権限がどれであってもプロセスから利用できません。この機能 を使っている例として、カーネルモジュールの読み込みを無効にする機能が挙 げられます。試験的ではありますが、この機能を駆使したツールに LCAP http: //pweb.netcom.com/~spoon/lcap/ があります。【訳註:LCAP は、カーネルが サポートしているケイパビリティを無効にすることによって、システムをより 安全にする仕組みです】 POSIX ケイパビリティについての詳しい情報は、 ftp://ftp.kernel.org/pub/ linux/libs/security/linux-privs で利用できます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.1.3. プロセスの生成とその操作 プロセスは fork(2) やお薦めできない vfork(2)、Linux 独自の clone(2)を使 って生成します。これらのシステムコールはすべて、既存のプロセスを複製し 、2 つのプロセスを生成します。プロセスは execve(2)、もしくはそのフロン トエンド(exec(3)や system(3)、popen(3) がその例)をコールして、別のプロ グラムを実行できます。 setuid や setgid してあるプログラムを実行すると、プロセスの euid や egid (それぞれ)には、そのファイルに設定してある値がセットされます。この 相関関係は、いにしえの Unix では競合状態を引き起こし、セキュリティ上の 弱点の源となっていました。以前は setuid や setgid してあるスクリプトに 対応していたからです。カーネルが、どのインタプリタが動作するのかを見る ためにファイルをオープンする時と、(id がセットされ)インタプリタに制御を 返ってインタプリタがファイルを再オープンしてスクリプトを解釈する時の間 に、攻撃者がファイルを操ってしまう可能性がありました(直接にもしくはシン ボリック・リンク経由で)。 Unix ライクなシステムは、setuid したスクリプトのセキュリティ上の問題に 対して、さまざまなやり方で対処してきました。あるシステムでは、実行スク リプトに setuid や setgid のビットが立っていると、それを完全に無視して います。Linux もこれに該当し、安全さは疑いようがありません。ごく最近の System V R4 や BSD 4.4 のリリースでは、カーネルの競合状態を避けるために 、また違ったアプローチをとっています。これらのシステムでは、カーネルは id がセットしてあるスクリプト名をインタプリタに渡す時に、パス名を渡さず (これが競合状態を起こすことになります)、/dev/fd/3 というファイル名を渡 します。これはスペシャルファイルで、既にそのスクリプトでオープンしてい ますので、攻撃者が悪用する競合状態は起こりえません。このようなシステム においても、安全さが必要なプログラムに setuid や setgid してあるシェル ・スクリプト言語を使うのには賛成できません。理由は下記で論じます。 プロセスがさまざまな uid や gid の値に変化をもたらすケースも存在します 。 setuid(2) や seteuid(2)、setreuid(2)、Linux 独自の setfsuid(2)を見て ください。特に保存ユーザ id(suid)という属性はそのケースに該当し、本当に 信頼されたプログラムが、一時的に uid を変更してしまいます。 Unix ライク なシステムでは、suid を下記のルールの下でサポートしています。 ruid の変 更もしくは euid が ruid と異なる値になった場合は、suid には新しい euid の値が設定されます。特権を持たないユーザは、自分の suid から自分の euid を、ruid から euid を、 euid から ruid を設定できます。 Linux 独自の fsuid プロセス属性は、NFS サーバのようなプログラムで、ファ イルシステムの権限に限って指定された uid に許可をあたえます。そのプロセ スへシグナルを送る許可は与えません。 euid が変更されると fsuid は新しい euid の値に変更されます。fsuid は setfsuid(2)という Linux 独自のシステ ムコールを使って設定できます。 root 以外から呼び出された場合は、fsuid には現在の ruid や euid、seuid、あるいは現在の fsuid しか設定できません 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2. ファイル すべての Unix ライクなシステムは、「/」を情報が保存されている大元とし、 そこから広がる形でファイル木構造をつくっています。ファイル木構造はディ レクトリを階層化していて、そのぞれぞれがファイルシステムのファイルシス テム・オブジェクト(FSO)を持っています。 Linux においては、ファイルシステムのオブジェクト(FSO)には通常のファイル やディレクトリ、シンボリックリンク、名前つきパイプ(ファーストイン・ファ ーストアウトと言ったり、FIFO と言ったりします)、ソケット(下記を参照して ください)、キャラクタスペシャル(デバイス)ファイル、ブロックスペシャル (デバイス)ファイル等があります(Linux では find(1)コマンドにその一覧があ ります)。その他の Unix ライクなシステムでもまったく同じ、もしくは似たよ うな FSO の一覧があります。 ファイルシステム・オブジェクトは、ファイルシステム上に存在し、ファイル 木構造にあるディレクトリにマウントしたりアンマウントしたりします。ファ イルシステムのタイプ(たとえば ext2 や FAT)とは、ディスク上にデータを配 置し、速度や信頼性等を最適化する、特定の約束事です。「ファイルシステム 」という用語をファイルシステムの種類と同じ意味で使う場合もよくあります 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2.1. ファイルシステム・オブジェクトの属性 ファイルシステムのアクセス制御の属性は多少変わっていて、アクセス制御は マウント時に選択したオプションに左右されます。 Linux では、ext2 がファ イルシステムとしては現状最も一般的ですが、おびただしい数のファイルシス テムをサポートしています。たいていの Unix ライクなシステムも、複数のフ ァイルシステムをサポートしています。Unix ライクなシステムは、それぞれで 複数のファイルシステムのタイプをサポートしています。 Unix ライクなシステムの多くは、少なくとも下記の情報を記録しています。 ・ 所有 uid と gid ――ファイルシステム・オブジェクトの「所有者」を識 別します。特に断りがない限り、普通は所有者もしくは root だけがアク セス制限に関する属性を変更できます。 ・ 許可ビット――ユーザ(所有者)、グループ、その他ごとに、読み込み、書 き込み、実行の権限を表すビット。通常のファイルの場合は、読み、書き 、実行という文字通りの意味を持ちます。ディレクトリの場合、「読み込 み」パーミッションはそのディレクトリの中を見られることを意味し、「 実行」パーミッションは、別名「検索」パーミッションとも言われ、実際 にそのディレクトリに入って、そこにあるものを使用できます。「書き込 み」パーミッションは、そのディレクトリでファイルの追加、削除、変更 ができます。追加だけを許可させたい場合は、下記に説明する sticky ビ ットを立てること。シンボリックリンクのパーミッションは、意味を持た ないことに注意してください。意味を持つのは、シンボリックリンクが存 在しているディレクトリとリンク先のファイル自身のパーミッション値だ けです。 ・ 「sticky ビット」――ディレクトリに設定されると、削除(移動)や名前の 変更は、ファイルの所有者やディレクトリの所有者もしくは root しか行 えなくなります。これは Unix 一般で利用されている拡張機能で、Open Group の Single Unix Specification version 2 に定めてあります。古い バージョンの Unix では「save program text」ビットと呼ばれ、メモリに 常駐する(スワップアウトしない)実行形式ファイルであることを示してい ました。これが行えるシステムでは、root だけがこのビットを設定できる ようにしていました(そうしないと、ユーザが「すべて」をメモリに入れて しまうことで、システムをクラッシュできてしまいます)。 Linux では、 このビットは通常のファイルに対して、何の影響も与えません。また一般 ユーザでも、自分が所有するファイルに対して設定が可能です。 Linux が 仮想メモリ管理を実装するにいたって、この機能はすたれてしまいました 。 ・ setuid、setgid ――実行形式ファイルに設定されると、実効 uid と gid にそのファイルの所有者 ID と gid が設定されます(各々独立に)。 Unix ライクなシステムは、この機能をすべてサポートしています。 Linux と System V では、setgid が実行権をまったく持たないファイルに設定され ると、そのファイルがアクセスされている時に、強制ロック(mandatory locking) がそのファイルにかかります(ただし、マウントしているファイ ルシステムが強制ロックをサポートしていれば)。この仕組みは負荷が非常 に重く、Unix ライクなシステムで広く採用されてはいません。実際には、 Open Group の Single Unix Specification version 2 の chmod(3)で、設 定が無意味なら、システムは実行権がないファイルに対して setuid を有 効にする要求を無視してもよいことになっています。 Linux や Solaris では、setgid がディレクトリに設定されると、そのディレクトリに作成さ れるファイルは、自動的にそのディレクトリの gid に設定し直されます。 この方法で実現できるのが、「プロジェクト用ディレクトリの作成」です 。特別に設定したディレクトリにユーザがファイルを保存すると、グルー プの所有者が自動的に変更されるようになります。しかし、ディレクトリ に setgid ビットを立てることは Single Unix Specification のような規 格として規定されているわけではありません[Open Group 1997]。【訳註: ファイルのロック機能には、強制ロック(mandatory locking)とアドバイザ リ・ロック(advisory locking)があります。違いは、前者がカーネルがプ ロセスを監視しロック操作を行うので、プロセス間の依存関係を越えてロ ックが可能です。これに対して後者は、プロセス自身がロック操作を行う ので、そのプロセスの制御外のものに対してはロックが無効となります。 詳しくは、カーネル付属のドキュメントの linux/Documentation/ mandatory.txt を参照してください】 ・ タイムスタンプ――ファイルシステム・オブジェクトには、アクセスした り、修正したりした時間を記憶してあります。しかし、所有者は自由にこ れらの値を変更できるので(touch(1)を参照)、この情報を安易に信頼しな いようにしてください。 Unix ライクのシステムは、すべてこの機能をサ ポートしています。 下記は、ext2 ファイルシステムを使っている Linux 独自の属性になります。 しかし、同じ機能を持った他のファイルシステムもたくさんあります。 ・ 変更不可(immutable)ビット――ファイルシステム・オブジェクトに対して 、いかなる変更も認めません。 root だけが設定と解除ができます。この 仕組みは、ext2 ファイルシステムだけがサポートしており、すべての Unix システム(場合によっては Linux ファイルシステムでも)で利用でき るわけではありません。 ・ 追加限定(append-only)ビット――許可されるのは、ファイルシステム・オ ブジェクトの追加だけです。 root だけが設定と解除ができます。この仕 組みは、ext2 ファイルシステムだけがサポートしており、すべての Unix システム(場合によっては Linux ファイルシステムでも)で利用できるわけ ではありません。 拡張機能で他に一般的なものは、「このファイルを消せない」ことを示すビッ トです。 上記の値は、マウント時に適用される場合が多いので、あるビット値が既に値 (媒体上の値が何であれ)を持っていたかのように扱われる場合もあります。詳 しいことは mount(1)を参照してください。これらのビットは役に立ちますが、 注意する必要があります。それは平易で使いやすいこと、ある行為を防げるの に十分なことです。たとえば、Linux においてはマウントする時に「noexec」 すると、そのファイルシステム上ではプログラムの実行ができなくなります。 man にも記述してある通り、これはそのシステムと互換性のないシステムで動 くバイナリがあるファイルシステムをマウントしようとする場合に使います。 Linux では、このオプションで誰かがファイルを実行することを完全には防ぎ きれません。そのファイルをどこかにコピーし、そこで実行できてしまいます し、「/lib/ld-linux.so.2」というコマンドを使って、そのファイルを直接実 行できてしまうからです。 ファイルシステムには、これらのアクセス制御に対するビット値をサポートし ていないものもあります。繰り返しますが、mount(1)を見て、ファイルシステ ムがどのように扱かわれるのか確認してください。とりわけ MS-DOS ディスク は、Unix ライクなシステムでサポートされているケースが多く、デフォルトで はほんのわずかな属性しかサポートされていません(またこれらの属性を定義す るのは普通とはいえません)。その場合、Unix ライクなシステムは標準的な属 性をエミュレートします(おそらく特別なファイルをディスク上に置いて、実装 しています)。またそれらの属性は、普通 mount(1) コマンドで調整できます。 注意すべき重要な点は、ファイルを追加したり、削除したりするのに関係して くるのが、そのファイルのパーミッションを表すビットとそのファイルがある ディレクトリの所有者だけ、という点です。その Unix ライクなシステムが、 より高度な手法(POSIX ACL のような)を持っていれば話は別ですが。システム に他の拡張機能がない場合(Linux 2.2 は普通は持っていません)はパーミッシ ョンを表すビットに何もパーミッションが無いファイルは、ディレクトリに許 可があるなら削除できてしまいます。また、親ディレクトリが子ディレクトリ にユーザやグループを変更できるようにしていると、そのディレクトリ配下の ものはすべて、そのユーザとグループに置き換えが可能になります。 セキュリティに関して、IEEE の POSIX 規格では、ACL に関してのきちんとし た技術を定義していて、ユーザやグループが持つパーミッションのリストをサ ポートしています。ただ残念なことに、広くサポートされていないだけでなく 、Unix ライクなシステムであってもきちんと同じ方法でサポートされていませ ん。たとえば、普通の Linux 2.2はファイルシステムに ACL はおろか、POSIX ケイパビリティの値すら持っていません。 Linux で注目に値するのは、デフォルトでは root ユーザに対して ext2 ファ イルシステムの領域をほんのわずかしか割り当てない点です。これは、不完全 ではありますがサービス拒否攻撃に対して防御になります。あるユーザが root ユーザと共有しているディスクを一杯にしたとしても、root ユーザには使い残 しでちょっとした領域が残ります(たとえば重要な機能のためにとっておけま す)。デフォルトでは、ファイルシステム領域の 5% が確保されています。 mke2fs(8) の「-m」オプションをよく見てください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2.2. 作成日時の初期値 作成日時には、下記のルールを適用しています。 Unix システムでは、新しい ファイルシステム・オブジェクトが creat(2) や open(2) で作成されると、 uid にはそのプロセスの euid が設定され、gid にはそのプロセスの egid が 設定されるケースが多くあります。 Linux では fsuid という拡張機能がある 分、少々異なっています。 uid にはそのプロセスの fsuid が、gid にはその プロセスの fsguid が設定されます。ディレクトリに setgid ビットが立って いるか、そのファイルシステムの grpid が立っていれば、gid はそのディレク トリの gid が実際には設定されます。ディレクトリに setgid する拡張機能は 、Sun Solaris や Linux を含む多くのシステムでサポートしています。先に書 きましたが、「プロジェクト」ディレクトリ(ある「プロジェクト」用のディレ クトリ)では、そのプロジェクト用に特別にグループを作成します。ディレクト リはそのグループが所有者で setgid してあります。そこにファイルを置くと 、自動的にプロジェクトが所有することになります。同様に、setgid ビットが 立っているディレクトリ中に新しいサブディレクトリを作成すると(ファイルシ ステムの grpid も設定していない)、新しいサブディレクトリにもその setgid ビットが設定されます(つまりプロジェクト用のサブディレクトリにも好都合で す)。これ以外のケースでは、setgid は新しく作成したファイルに対しては普 通に振る舞います。「ユーザプライベートグループ」という仕組みの理論的な 根拠がここにあります(Red Hat Linux 他で使われています)。この仕組みにお いては、メンバーそれぞれは自分たちだけで構成している「プライベート」グ ループに属していて、デフォルトではそのグループにどのファイルの読み書き も可能としています(グループに属しているのは自分たちだけなので)。このよ うに、ファイルのグループ構成メンバーが継承されると、読み書きの権限も継 承されるわけです。【訳註:ユーザプライベートグループについては、この説 明だけではわかり難いので、ユーザプライベートグループ も参照してみてください】基本的なフ ァイルシステム・オブジェクトのアクセス制御の値(読み書き、実行)は、 (要 求された値 & ~ プロセスの umask)から割り出します。新しいファイルでは、 sticky ビットも setuid ビットも常に立っていません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2.3. アクセス制御属性の変更 chmod(2)や fchmod(2)、chmod(1) を使って変更できますが、chown(1)や chgrp (1) も見てください。 Linux では、Linux 独自の属性を chattr(1)で操作でき るものもあります。 Linux で注意する点は、root だけしかファイルの所有者を変更できない点です 。 Unix ライクなシステムには、一般ユーザでも他のユーザにファイルの所有 権を移動できてしまうものもあります。これは混乱を招くもとなので、Linux では許していません。たとえば、ディスク使用量の制限をかけたい場合にその ような操作ができてしまうと、大きなファイルは他の人(犠牲者)のせいだ、と いう言い訳を許してしまうことになります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2.4. アクセス制御属性を使用する Linux や他の Unix ライクなシステムの大半では、読み書きの属性値はファイ ルをオープンした時にだけチェックされます。つまりその後に読み書きしても 、再チェックは行われません。しかし、ファイルシステムは Unix ライクなシ ステムの根幹なので、システムコールを何度も呼び出すことで、これらの属性 をチェックしています。これらの属性をチェックするシステムコールには、 open(2)や creat(2)、link(2)、 unlink(2)、rename(2)、mknod(2)、symlink (2)、socket(2) があります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2.5. ファイルシステムの階層 長年の慣例で、「何のファイルはどこに置く」という約束事があります。出来 るだけきまりを守って、ディレクトリ階層の中に情報を格納してください。た とえば、全体に関連する設定情報は /etc に置いてください。 Filesystem Hierarchy Standard (FHS)は、この慣例を論理的に定義しようとしており、 Linux システムは広く採用しています。 FHS は従来の Linux Filesystem Structure standard (FSSTND)をアップデートしたもので、Linux や BSD、 System V から知恵を拝借しています。 http://www.pathname.com/fhs に FHS の情報がありますから、ご覧ください。概略については Linux では hier(5)を 、Solaris では hier(7)を参照してください。慣例は時には一致しない場合が ありますが、コンパイルやインストール時に可能な限り融通が効くようにして おいてください。 Linux Standard Base が、FHS を採用したことに 触れておきます。この組織は Linux ディストリビューション間の互換性を高め 、準拠した Linux システムならどんなソフトウェアでも動作するように規格を 作り、それを促進する機関です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.3. System V IPC Linux や System V を含む Unix ライクなシステムの多くは、System V のプロ セス間通信(IPC)をサポートしています。事実、System V IPC は Open Group の Single UNIX Specification Version 2 で必須になっています[Open Group 1997]。 System V の IPC は 3 つのものから構成されています。メッセージ・ キュー、セマフォ、共有メモリがそれです。それぞれの属性は下記の通りです 。 ・ 作成者や作成者が属するグループ、その他の者の読み書きのパーミッショ ン。 ・ 作成者 uid と gid ――作成者の uid と gid。 ・ 所有者 uid と gid ――所有者の uid と gid(初期状態では作成者の uid と同じ)。 下記のルールにもとづいてアクセスします。 ・ プロセスが root の権限を持っていれば、アクセスは許可されます。 ・ プロセスの euid が所有者もしくは作成者の uid と同じなら、作成者のパ ーミッションを見て、問題なければアクセスが許可されます。 ・ プロセスの euid が所有者もしくは作成者の gid と同じ、もしくはプロセ スの属するグループの中に所有者もしくは作成者の gid と同じものがあれ ば、作成者のパーミッションを見て、問題なければアクセスが許可されま す。 ・ 以上に当てはまらなければ、「その他のユーザ」のパーミッションをチェ ックします。 root もしくは所有者や作成者の euid を持つプロセスは、所有者の uid や gid を設定でき、また削除も可能であることを忘れないでください。詳しくは ipc(5)を参照してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.4. ソケットとネットワーク接続 ソケットは情報を伝える手段として、特にネットワーク越しの通信に使用され ています。ソケットは、そもそも Unix システムの流れの 1 つである BSD 系 が開発しました。しかし他の Unix ライクなシステムに対しても、およそ移植 しやすくなっています。 Linux や System V 系はソケットを BSD と同レベル でサポートしており、Open Group の Single Unix Specification [Open Group 1997]でも必須とされています。以前 System V システムでは、ネットワーク越 しの通信インタフェースを別に持っていました(互換性がありませんでした)が 、Solaris のようなシステムもソケットをサポートしているのは注目に値しま す。 socket(2)は情報を伝えるための接続ポイントを作成し、それを示すディ スクリプタを返します。これはファイルに対する open(2)とある意味同じです 。ソケットのパラメタには、プロトコル・ファミリーとタイプを指定します。 つまりインターネット・ドメイン(TCP/IP version 4)や Novell の IPX もしく は「Unix ドメイン」というように。サーバは、通常は bind(2)を呼んでから listen(2)を呼び、accept(2) もしくは select(2)します。クライアントはとい うと、bind(2)(省略される場合あり)を呼び、connect(2)します。詳しい情報は それぞれの man を参照してください。ソケットを利用するのに man だけでは 難しいかもしれません。 Hall "Beej" [1999]のような資料を読んで、どうやっ てこれらのシステムコールを組み合わせて利用するのかを学ぶのもよいと思い ます。 「Unix ドメイン・ソケット」はネットワーク・プロトコルに実際は該当しませ ん。というのは、同一マシン上で接続するに過ぎないからです。 (このドキュ メントを書いている時点の標準的な Linux カーネルでは) ストリームとしてこ のソケットを使う場合、名前つきパイプと非常に似てはいますが、際だった長 所があります。 Unix ドメイン・ソケットがコネクション指向である点に注目 してください。ソケットに対する新しい接続は、それぞれ新しい接続チャネル となります。この点が名前つきパイプとは大きく異なる点です。この特性によ って、Unix ドメイン・ソケットは、名前つきパイプの代わりに使われ、IPC を 実装し、重要なサービスを広く提供しています。名前なしパイプもそうなので すが、socketpair(2)を使って、名前なしの Unix ドメイン・ソケットも使えま す。名前なし Unix ドメイン・ソケットは、名前なしパイプとある程度似てい るので、IPC に使えます。 Unix ドメイン・ソケットには、セキュリティに関する興味深い点がいくつかあ ります。まず、Unix ドメイン・ソケットはファイルシステム上に存在するよう に見え、stat(2) も利用できますが、open(2)では開けません(socket(2)とその 仲間のインタフェースを使わなければいけません)。次に、Unix ドメイン・ソ ケットはファイル・ディスクリプタでプロセス間をやりとりします(ファイルの 中身ではなく)。この変わった機能は、これまで色々な場面で使われてきました 。他の IPC の手段では、この機能を利用できません(ディスクリプタは、そも そもコンピュータ・サイエンスの言葉で言う「ケイパビリティ」の制限版とし て利用できます)。ファイルディスクリプタを sendmsg(2)を使って送り、そこ にある msg(メッセージ) フィールドに当たる msg_control が control message のヘッダ配列を指し示しています(msg_controllen フィールドは配列 が何バイトあるのかを指定していなければいけません)。 control message は cmsghdr 構造体になっていて、制御データの後に実データが続いています。こ の目的に使うなら、cmsg_type に SCM_RIGHTS を設定してください。ファイル ディスクリプタは recvmsg(2) を使って取り出し、後は似たような方法でデー タに行き着きます。正直いうと、この機能はとても奇をてらった方法ですが、 知っておいても無駄ではありません。 Linux 2.2 以降で Unix ドメイン・ソケットにさらに機能が追加されています 。それは相手側の「認証」ができる点です(pid や uid、gid を利用可能)。サ ンプルコードは下記のようになります。 /* fd= file descriptor of Unix domain socket connected to the client you wish to identify */ struct ucred cr; int cl=sizeof(cr); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) { printf("Peer's pid=%d, uid=%d, gid=%d\n", cr.pid, cr.uid, cr.gid); 標準的な Unix での慣例では、TCP や UDP のローカルなポート番号は 1024 よ り小さく、root の権限が必要になります。一方プロセスは 1024 以上のポート であれば、制限無しにつなげられます。 Linux はこの慣例を踏襲していて、さ らにプロセスは CAP_NET_BIND_SERVICE というケイパビリティが必要となり、 これで 1024 以下のポート番号に接続できます。通常このケイパビリティは、 euid が 0 であるプロセスだけが持つことができます。冒険好きな人なら、 Linux のソースを調べればわかります。 Linux 2.2.12 なら /usr/src/linux/ net/ipv4/af_inet.c にある関数の inet_bind()がそれです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.5. シグナル シグナルは、Unix ライクな OS 世界における単純な「割り込み」方式で、 Unix では古くからある機能の 1 つです。プロセスは、「シグナル」を別のプ ロセスに送れ(たとえば kill(1)や kill(2) を使って)、その相手のプロセスは それを受取り、非同期にそのシグナルを扱います。プロセスが他のプロセスに 任意のシグナルを送るには、送り手のプロセスが root もしくは、相手のプロ セスの実もしくは実効ユーザ id の権限を持っていなければいけません。しか し、シグナルを別の方法で送ることもできます。特に SIGURG はネットワーク 越しに TCP/IP のアウト・オブ・バンド(out-of-band (OOB))メッセージを使っ て送れます。【訳註:「アウト・オブ・バンド」とは、データとは独立の通信 経路を使って、制御用情報を交換することです】 シグナルはいにしえからの Unix の機能の 1 つですが、実装も書き方もさまざ まです。「シグナルを処理している時に、他のシグナルが発生したらどうなる か?」という根本的な問題を抱えています。 libc5 を採用している古い Linux ではシグナルのいくつかで操作方法が最新の GNU libc ライブラリと異なる点 があります。シグナルハンドラで C ライブラリ関数を安全に呼び出せない場合 がよくあり、システムコールの中にさえ、安全でないものがあります。ドキュ メントを確認して、シグナルから呼び出しても安全であることが保証されてい るかを確認してください。詳しい情報は、glibc FAQ(ローカルに /usr/doc/ glibc-*/FAQ というコピーがあるシステムもあります)を見てください。 新しくプログラムを書くなら、POSIX シグナルシステム(BSD のものにかわっ て) を使ってください。この仕組みは広くサポートされていて、古いシグナル システムが持っていた問題のいくつかを解決しています。 POSIX シグナルシス テムは、sigset_t というデータ型を使うことを前提にしていて、そのデータ型 を扱う関数によって操作できます。その関数は、 sigemptyset()と sigfillset ()、sigaddset()、sigdelset()、sigismember()です。 sigsetops(3)にこれら の関数についての説明があります。設定したなら、sigaction(2)や sigprocmask(2)、sigpending(2)、 sigsuspend(2)を使って、シグナルの操作を 設定してください(詳細な情報は man を見てください)。 通常はシグナルハンドラをどれもできるだけ短くかつ単純にし、競合状態に注 意を向けてください。シグナルはそもそも非同期に発生するので、恐らく競合 状態が起こるでしょう。 サーバにはある共通した慣例があります。SIGHUP を受けた場合には、ログファ イルをすべて閉じ、設定ファイルを再オープンして読み込み、再びログファイ ルを開きます。これでサーバを止めず再設定が行え、データをなくすことなく ログをローテーションできます。何らかのサーバを書いていて、この慣例をな るほどと思うなら、ぜひこの機能をサポートしてください。 Michal Zalewski [2001] はどうやってシグナルハンドラが攻撃を受けるかにつ いて、素晴らしいチュートリアルを書きました。その中でシグナルの競合状態 をいかに排除するかについて、アドバイスをしています。さらに情報を得たい なら要約を読むようにおすすめします。ここに書くものは、私が推奨すること ですが、Michal 氏のものと同様です。 ・ 可能な限りどんな場合でも、シグナルハンドラは特定のフラグを設定する だけにして、他に何もさせないようにしてください。 ・ より複雑なシグナルハンドラを実装せざるを得ないなら、シグナルハンド ラで使用しても安全である、特に指定があるものだけを利用するようにし てください。特に、C の malloc() や free()(シグナルから保護されてい ないシステムが大半) だけでなく、malloc() と free()に依存しているた くさんの関数(printf() ファミリーや syslog()等)を使用しないでくださ い。「ラッパー」を作って、安全でないライブラリを呼び出すことも可能 です。ラッパーで再入を防ぐためにグローバルなフラグをチェックします 。しかしお勧めはしません。 ・ プログラム中でアトミックでない操作を行っている間は、シグナル送出を ブロックし、シグナルハンドラ内部でもシグナル送出をブロックしてくだ さい。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.6. Quota とリソースの制限 Unix ライクなシステムの多くは、ファイルシステムの割り当て制限(quota)と プロセスのリソース制限を行なう機能を持っています。もちろん Linux も持っ ています。この仕組みはサービス拒否攻撃を防ぐのに特に役立ちます。各ユー ザが利用できるリソースを制限することで、単独ユーザがシステム全体のリソ ースを食い尽くしにくくできます。この機能には、「ハードな制限」(hard limit)と「ソフトな制限」(soft limit)両方の意味があり、多少意味が異なる ので、注意が必要です。 記憶装置(ファイルシステム)の割り当て制限は、マウントポイント毎に設定が 可能で、特定のユーザやグループがそこで使用できるブロック数やファイル数 (inode数)に制限をかけられます。「ハードな」ものが制限を越えることができ ないのに対して、「ソフトな」ものは一時的に制限を越えることが許されてい ます。 quota(1)、quotactl(2)、quotaon(8)を参照してください。 rlimit は、プロセスに対する数々の割り当て制限を実現する仕組みで、ファイ ルサイズや子プロセス数、オープンできるファイル数等を扱えます。「ソフト な」制限(現状の制限(current limit)とも言う)と「ハードな制限」 (上限 (upper limit)とも言う)があります。ソフトな制限を超えることは決してでき ませんが、システムコールによってハードの上限までもっていくことができま す。 getrlimit(2)や setrlimit(2)、getrusage(2)、sysconf(3)、ulimit(1)を 参照してください。制限をかける方法がいくつかあることに注意してください 。PAM モジュールである pam_limits もその 1 つです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.7. ダイナミックリンク・ライブラリ プログラムを実行するには、実際ライブラリが必要となります。 Linux を含む 最近の Unix ライクなシステムの大半は、プログラムはデフォルトでダイナミ ックリンク・ライブラリ(DLL)を使ってコンパイルしてあります。つまりあるラ イブラリを更新すれば、そのライブラリを使っているプログラムすべてが新し い(うまくいけばより良い)バージョンになります。 普通ダイナミックリンク・ライブラリは、特別なディレクトリ数ヶ所に存在し ます。通常は、/lib や /usr/lib、 PAM モジュールの /lib/security、 X Window System の /usr/X11R6/lib、 /usr/local/lib がそれに当たります。プ ログラムは、この標準的な慣例を使うようにしてください。デバッグ時を除い ては、カレントディレクトリからダイナミックリンク・ライブラリが存在する 所をたどっていけるような値を使わないでください(攻撃者が好みの「ライブラ リ」を追加できてしまうかもしれません)。 ライブラリに名前を付けたり、シンボリックリンクを張る場合に特別な約束事 が存在します。その結果として、ライブラリを新しくしても、古くて、バック ワード・コンパチビリティがないバージョンのライブラリをサポートできます 。また、特定のライブラリや特別なプログラムを実行する時に、あるライブラ リの特定の関数だけを変更する方法も複数あります。 Unix ライクなシステム が Windows ライクなシステムと比べて、極めて優れている点がこれです。Unix ライクなシステムはライブラリの更新を行う点では、より優れたシステムだと 思います。この優れた点が一因となり、Windows ベースのシステムより Unix や Linux システムの方が安定していると考えています。 Linux システムすべてを含む GNU glibc ベースのシステムでは、ディレクトリ の一覧は /etc/ld.so.conf に保存してあり、プログラムは起動する間に自動的 に検索しています。 Red Hat がベースとなっているディストリビューションで は、普通は /usr/local/lib は /etc/ld.so.conf に記述してありません。私は これをバグの 1 つだと見なしています。/usr/local/lib を /etc/ld.so.conf に追加するのは、プログラムの多くを動かすのに必要な作業です。この作業は 、Red Hat がベースとなっているシステムで共通の「フィックス」作業になっ ています。あるライブラリの関数をいくつかだけ変更したいが、その他はその まま使いたい場合は、優先して使いたいライブラリ(.o ファイル)の名前を / etc/ld.so.preload に記入してください。この「先読み(preloading)」ライブ ラリは、標準で用意してあるものに先がけてロードされます。この先読みファ イルは、緊急パッチ用によく使います。ディストリビューションでは、配布時 にはそのようなファイルは使用しません。起動時にディレクトリをすべて検索 するのはあまりに時間の無駄なので、実際にはキャッシュを使って処理してい ます。 ldconfig(8)はデフォルトで /etc/ld.so.conf を読んで、ダイナミック リンク・ライブラリのあるディレクトリにシンボリック・リンクを適切に張っ て設定し (したがって、標準的な慣例にしたがった方が良い)、キャッシュ情報 を /etc/ld.so.cache に書きます。他のプログラムはそのキャッシュを利用し ます。つまり、ldconfig は DLL が追加されたり、DLL が削除されたり、DLL のディレクトリごと変更されたりした場合には必ず動かさなければいけません 。ライブラリをインストールした時にパッケージ・マネージャが作業の 1 つと して ldconfig を動かす場合がよくあります。プログラムを起動すると、ダイ ナミック・ローダーを使って /etc/ld.so.cache を読み、必要となるライブラ リをロードします。 さまざまな環境変数がこの過程をコントロールできます。実際、この過程を変 更してしまう環境変数も存在します(たとえば、一時的に別のライブラリに置き 換えて、実行できます)。 Linux では、環境変数 LD_LIBRARY_PATH はコロン (:)で区切って記述してあるディレクトリの集まりで、ライブラリをまずここか ら検索し、その後に標準的なディレクトリを検索します。新しいライブラリを デバッグしたり、特定用途のために非標準のライブラリを使用する時に役に立 ちます。ただ、そのディレクトリを管理できる人間を信頼することになります 。注意してください。環境変数 LD_PRELOAD はオブジェクトファイルの一覧で 、標準的なライブラリから変更する関数を含んでいます。/etc/ld.so.preload がまさにそれです。環境変数 LD_DEBUG はデバッグ情報を表示します。「all」 と指定すると、ダイナミックリンクしているプロセスについて、実行中に膨大 な情報を表示します。 ユーザがダイナミックリンク・ライブラリをコントロールできるようになると 、何らかの手当てをしない限り、setuid や setgid したプログラムが面倒なこ とになります。そのため、GNU glibc の実装では、プログラムに setuid や setgid してあるとこれらの環境変数(加えて同じような変数も)を無視するか、 動作に大幅な制限をかけます。 GNU glib ライブラリは、プログラムの権限を チェックして setuid もしくは setgid を判断しています。 uid と euid が違 っているか、gid と egid が違っていれば、ライブラリはそのプログラムが setuid もしくは setgid(もしくはそれを継承)していると仮定し、その結果、 機能を大幅に制限し、リンクを管理します。 GNU glib ライブラリをロードし てみればわかります。特に elf/rtld.c と sysdeps/generic/dl-sysdep.c を見 てください。 uid と gid を euid と egid に等しくしてプログラムを呼び出 せば、環境変数は機能をすべて働かせます。他の Unix ライクなシステムでは 、理由は同様である点は別として、状況が変わります。setuid もしくは setgid したプログラムは環境変数にむやみに影響を受けるべきではありません 。 Linux システムについては、私の著作である Program Library HOWTO から情報をさらに得られます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.8. Audit(監査) Unix ライクなシステムは、それぞれ異なる方法で監査しています。 Linux で 最も一般的な「監査」の仕組みは syslogd(8)で、klogd(8)とともに動いていま す。 wtmp(5)や utmp(5)、lastlog(8)、acct(2)も参照することをお勧めします 。サーバプログラム(Apache Web サーバのようなもの)は、独自に痕跡を監査す る仕組みを持っているものもあります。 FHS によれば、監査ログは /var/log もしくは、そのサブディレクトリに保存した方がよい、としています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.9. PAM Sun Solaris やほとんどすべての Linux システムでは認証が必要な時に、 Pluggable Authentication Modules(PAM: 差し替え可能な認証モジュール)を使 用します。 PAM を使えば、実行時に認証方法の設定を変更できるようになりま す(たとえばパスワードやスマートカード等の使用)。 PAM の利用方法について 詳細な情報は、Section 10.6 を見てください。【訳註:スマートカード(smart card)とは、プラスティックのカード上に IC やメモリ等のチップを載せたカー ドを指します。日本では IC カードと呼ぶケースが多いようです。従来の磁気 カードと比べると、より多くの情報を格納できるだけではなく、プログラムを インストールして実行することが可能である点が大きく異なります】 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.10. Unix ライクなシステムに固有なセキュリティ拡張機能 Unix ライクなシステムを拡張すべく、さまざまなコミュニティがいろいろな調 査や開発に力を注いでいます。たとえば、Unix ライクなシステムのいくつかで は機能を拡張して、米軍が求める階層構造を持ったセキュリティをサポートし ています。ソフトウェアを開発するなら、設計する時にこの拡張機能を実現で きるように頑張ってみてください。 FreeBSD は新しいシステムコールである jail(2) を持っています。 jail というシステムコール は環境を複数に分割し、仮想マシンをたくさんサポートします(ある意味、「ス ーパー chroot」と言えます)。利用方法としては、インターネット・サービス ・プロバイダの環境で、仮想マシンのサービスとして利用するのが大半です。 1 つの jail の内部では、すべてのプロセス(root が所有者であっても)はその jail の範囲に命令が限定されます。 FreeBSD システムを新規インストールし た後にブートすると、 jail に入るプロセスは 1 つもありません。プロセスが jail に入ると、そのプロセスとそこから派生するプロセスはすべて jail に入 ります。 jail に入ってしまえば、ファイル名空間へのアクセスは chroot(2) スタイルで制限を受けます(chroot を避けようとしてもブロックされます)。ネ ットワークリソースを利用する機能は、特定の IP アドレスに制限され、シス テムリソースの操作や権限をいじる行為は、大幅に切り詰められ、他のプロセ スとのやりとりは、同じ jail の中のプロセスにだけ制限されます。 jail そ れぞれは、1 つの IP アドレスを使っています。jail の中のプロセスは、他の IP アドレスを使って外部とやりとりはできない点に注意してください。【訳註 :jail(2)の日本語マニュアルはhttp://www.jp.freebsd.org/cgi/mroff.cgi? subdir=man&lc=1&cmd=&man=jail&dir=jpman-5.0.0%2Fman§=0 にあります】 Linux では拡張機能が利用可能です。たとえば先に論じた POSIX ケイパビリテ ィやマウント時の特殊なオプションがそれに当たります。 Linux システムで実 行環境を制限する試みをいくつか挙げておきます。アプローチの仕方はさまざ まです。 U.S. National Security Agency(NSA)では Security-Enhanced Linux (Flask) を開発し、特殊な言語でセキュリティ ・ポリシを定義し、それに基づきポリシを実施します。 Medusa DS9 は Linux を拡張し、カーネルレベルでユーザ空間で動く認 証サーバをサポートしています。 LIDS は、ファイル やプロセスを保護し、管理者にシステムを「封鎖(lock down)」する権限を与え ています。「ルールベースのアクセス制御」システム RSBAC は、アクセス制御に汎用的なフレームワーク(Generalized Framework for Access Control (GFAC))を用いています。これは Abrams 氏と LaPadula 氏によって作られ、複数のカーネルモジュールによって、柔軟なアク セス制御を実現しています。 Subterfugue は、「ソ フトウェアの実態をもって監視、実行する」というフレームワークです。サン ドボックスやトレーサ等を実行することで、システムコールを横取りし、その パラメタや返り値を変更します。 Linux 2.4 で動作し、何も変更はいりません (カーネルモジュールの追加は何も必要ありません)。 Janus はセキュリティツールで、制限された実行 環境下で信頼できないアプリケーションをサンドボックスに閉じ込めます。 User-mode Linux は、サンドボッ クスの実装の 1 つで、「Linux 上で Linux を動かす」ことを実現しています 。このようにさまざまなアプローチによって、より洗練したセキュリティモデ ルを実装していますので、Linus Torvalds 氏は、異なるセキュリティ・ポリシ でも採用できるような汎用的アプローチを開発して欲しい、と要望しています 。詳しくは、 http://mail.wirex.com/mailman/listinfo/ linux-security-module を見てください。 さまざまな Unix ライクなシステム上で、他にもいろいろなセキュリティ上の 拡張が存在しています。しかし、このドキュメントでは範囲外なので扱いませ ん。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 4. 入力されるものすべてを検証すること あなたは悪い道から救い出され、暴言 をはく者を免れることができる。 旧約聖書箴言 2 章 12 節 入力には、信頼できないユーザからのものもあります。そこで、使用する前に それらを検証(選別)する必要があります。まず何が正しいかを定義して、その 定義にマッチしないものすべてを拒否するようにしなければいけません。その 逆の定義の仕方をしてはいけません(何が不正かを定義し、それらを拒否する) 。なぜなら、重大なケースをうっかり定義し忘れてしまうかもしれないからで す。 ですが、検証コードが完全なのかを確認するために、テスト用(たいていは頭の 中で実行)で「不正な」値を定義するのは良いことです。私は入力フィルタを設 定した時には、頭の中でフィルタを攻撃してみて、不正な値がフィルタを通り 抜けないかを見てみます。入力内容にもよりますが、ここでは代表的ないくつ かの「不正な値」の例をあげてみます。これらは入力時にフィルタで防御する 必要がある値です。空文字や「.」、「..」、「../」、「/」や「.」ではじま る文字列や「/」もしくは「&」を含む文字列、すべての制御文字(特に NIL や 改行)もしくは「ハイビット」の文字(特に十進数で 254 と 255)がそれです。 繰り返しますが、コードは「悪い」値でチェックすべきではありません。頭の 中であなたが書いたパタンが容赦なく入力を制限して、正しい値だけを通すの かどうかを確かめるためです。もしそのパタンで十分に制限していなければ、 注意深くパタンを再調査して、他の問題がないか確認する必要があります。 最大文字数(適切なら最小文字数も)を制限して、文字数を超えても制御不能に ならないようにしてください (バッファオーバーフローについての詳細は Chapter 5 を見てください)。 ここではデータタイプとしてよく使われるものをいくつか挙げます。信頼でき ないユーザからのデータを利用する前に、必ず検証するようにしてください。 ・ 文字列に対しては、正しい文字やパタン(たとえば、正規表現として)を識 別し、それに沿わないものすべてを拒否してください。文字列に制御文字 (特に改行や NIL)やメタキャラクタ(特にシェルのメタキャラクタ) がある と特殊な問題が発生します。入力があったら、速やかにメタキャラクタを 「エスケープ」するのが最善です。入力が間違って渡らないようにするた めです。 CERT はそれ以上に、[CERT 1998、CMU 1998] にあるエスケープ する必要のない文字のリストに載っていない文字すべてをエスケープする ように推奨しています。メタキャラクタについての詳しい情報は、Section 7.3 を見てください。 ・ 数字すべてに対して、許容できる最小値(たいていはゼロ)と最大値を設け てください。 ・ 電子メールのアドレスを完全にチェックするのは、現実的にとても困難で す。というのも、すべてのケースを真面目にサポートしようとすると、ア ドレスの中には正しい形式ではあるものの、非常に複雑な検証を必要とす るアドレスが存在するからです。もしそのようなチェックが必要なら、詳 細は mailaddr(7)と IETF RFC 822 [RFC 822]を見てください。たいていは 、「一般的な」インターネット・アドレス形式だけを単純に許可すればよ いでしょう。【訳注:IETFは、Internet Engineering Task Force の略称 で、インターネットに関連する技術の標準化を進めるために設立された団 体です。ここが発行する文書が RFC(Requests For Comment)です】 ・ ファイル名をチェックしてください。普通、信頼できないユーザからは、 「..」 (上位ディレクトリ)という値を正しいものとして受け取りたくない でしょう。しかし、それは環境に依存しています。また、許可した文字だ けを載せてもよいかもしれません。特に改行(問題を起こす可能性がありま す)は、問題なければ削除してください。ファイル名においては、ディレク トリにおけるどんな変更も禁止するのが最善の策です。たとえば、「/」を 正しい文字として扱わない等。「glob」をサポートしてはいけません。つ まりファイル名を拡張するような「*」や「?」、「[」(「]」に対応)」さ らに「{」(}に対応)」等です。たとえば、「ls *.png」というコマンドは 「*.png」を全 PNG ファイルのリストに glob します。 C の fopen(3) コ マンド(たとえば)は、glob しませんが、コマンドシェルは、デフォルトで glob します。また C では(たとえば)glob(3)を使って glob 機能を利用で きます。 glob が必要なければ、 glob しないシステムコール(たとえば fopen(3)だけをできるだけ使用するか、無効にしてください(たとえばシェ ルで glob する文字をエスケープする)。 glob を許可するなら、細心の注 意を払ってください。 glob は便利に使えますが、glob を複雑にするとコ ンピュータにかなりの負荷をかけることになります。たとえば、ftp サー バには glob 命令をいくつか要求すると、いとも簡単にマシン全体でサー ビス拒否攻撃状態になるものもあります。 ftp> ls */../*/../*/../*/../*/../*/../*/../*/../*/../*/../*/../*/../* glob を許可しているにもかかわらず、glob するパタンに制限をかけない と、困ったことになるでしょう。そのようなプログラムは、独立したプロ セスとして走らせ、プロセスが消費する CPU 利用量やその他リソースに必 ず制限をかけてください。 Section 6.4.8 にこの方法についてのさらに詳 しい情報があります。また、Section 3.6 にこれらの制限のかけ方につい て詳しい情報がありますので、見てください。 ・ URI(URL を含む)が妥当なのか、チェックしなければいけません。ある URI を直接操作するなら(つまり、Web サーバや Web サーバもどきのプログラ ムを実装していて、要求されるデータが URL の場合)、URI が正しいかど うかを確認しなければなりません。また URI で特に注意を払うケースは、 ドキュメントルート(サーバが返すファイルシステム領域)を「回避」しよ うとするものです。ドキュメントルートを回避する最も一般的な方法は、 「..」やシンボリックリンクを経由する方法です。したがって大半のサー バは、どんな「..」ディレクトリもチェックしており、シンボリックリン クは特に指示がない限りは無視します。また、エンコード(URL エンコード や UTF-8 エンコード)してあるものは、まずデコードすることを忘れない でください。でないとエンコードされた「..」がすり抜けてしまいます。 URI は UTF-8 エンコードが入ることを前提としていませんので、ハイビッ ト文字が入った URI すべてを拒否するのが最も安全です。 データとして URI(URL)を扱うシステムの実装をしているなら、簡単にでき るとは夢々思わないでください。悪意あるユーザが他のユーザに迷惑をか けるような URI を入れ込むことが絶対ないようにしなければいけません。 さらに詳しい情報は Section 4.10.4 を見てください。 ・ クッキーで値を受けとった時には、利用しているクッキーがどんなもので あっても、ドメイン値が予期した値であることを必ずチェックしてくださ い。さもないと、 (おそらくクラックされた)関連サイトが偽のクッキーを 入れ込んでしまう可能性があります。ここでは、このIETF RFC 2965 で書 いてある例をあげます。このチェックをしくじるとどのような問題が生じ るかについて記述してあります。 □ ユーザ側が victim.cracker.edu にリクエストを出し、クッキーが session_id="1234" と返ってきて、デフォルトのドメインを victim.cracker.edu に設定します。 □ ユーザ側は spoof.cracker.edu にリクエストを出し、クッキーが Domain=".cracker.edu" で session-id="1111" と返ってきます。 □ 再度ユーザ側は victim.cracker.edu にリクエストを出し、下記を渡 します。 Cookie: $Version="1"; session_id="1234", $Version="1"; session_id="1111"; $Domain=".cracker.edu" victim.cracker.edu のサーバは、2 番目のクッキーがドメイン属性が 自分のそれと違っていることから、自分のものではないことを検知し 、無視しなければいけません。 問題を解決できないなら、正しい文字パタンには、プログラム内部や最終的な 出力に対して特別な意味を持つ文字もしくは文字列を含めてはいけません。 ・ ある文字の連続が、プログラムの内部に持っている書式に対して、特別な 意味を持つ場合があるかもしれません。たとえば、保存するデータに区切 りのある文字列を使うなら(内部でも外部でも)、区切り文字をデータ値と することを必ず禁じてください。テキストファイルに保存してあるデータ に、カンマ(,)やコロン(:)を区切りとして使っているプログラムはたくさ んあります。入力に区切り文字が入っていて、プログラムがそれに対処(す なわちそれを阻むか、何らかの方法でエンコードする)していなければ、問 題が発生するかもしれません。他の文字でも、これらの問題が発生する場 合がよくあります。それは、他の文字の中にシングルもしくはダブル・ク ォーテーション(文字列を囲むのに使用)や小なり記号「<」(SGML や XML、 HTML ではタグの開始を示す識別に使われています。これらのフォーマット でデータを保存する場合、この記号は重要です)が入っているケースです。 大半のデータフォーマットは、エスケープシーケンスを用意して、このよ うなケースに対処しています。そのエスケープシーケンスを使うか、入力 データをフィルタしてください。 ・ ユーザに対してある文字の連続が戻される場合に、特別な意味を持つケー スがあります。一般的な例として、HTML のタグを入力として認める場合、 後になってそれを他のユーザにポストすることがあります(たとえば、ゲス トブックや「読者のコメント」コーナー)。しかし、この問題は広く影響を 及ぼしています。この話題についてさらに全体的な議論は、 Section 6.15 を、HTML のフィルタリングについて特化した議論は Section 4.10 を見て ください。 これらのテストは 1 ヶ所で集中して行ってください。そうすれば、そのテスト が正しいかどうか、後になって簡単に調査できます。 正しい入力をチェックするテストが、予定した通り確実に動作するようにして ください。別のプログラムが使う入力(ファイル名や電子メールアドレス、URL 等)をチェックする場合には特に重要です。これらのプログラムは、見落としが ちな間違いを抱えていることが多く、いわゆる「代理人問題」(データを実際に 使用するプログラムとチェックするプログラム間で前提条件が異なっているケ ース)が発生します。適切な規準があるなら、それを見てください。あわせて、 そのプログラムが、拡張機能を持っていないかどうかの調査もしてください。 拡張機能は知っておく必要があります。 ユーザの入力を解析している間は、一時的に特権すべてを落とすというのは良 い考えです。また独立したプロセスを作成するのも、同じく良い考えです (解 析を行う場合は常に特権を落とし、他のプロセスが解析の要求に対してセキュ リティ上のチェックを行う)。このケースがとりわけ当てはまるのは、解析作業 が複雑である場合(たとえば、lex や yacc といったツールを使う)や、プログ ラミング言語がバッファオーバーフローを防げない場合です(たとえば、C や C++)。特権を最小限にする方法については Section 6.4 を見てください。 セキュリティ上の判断を行う際にデータを使用する時には(たとえば「このユー ザを通過させなさい」)、必ず信頼できる経路を使ってください。たとえば、公 開されたインターネット上では、マシンの IP アドレスやポート番号だけにユ ーザの認証を任せてはいけません。というのは、この情報を(もしかすると悪意 を持った)ユーザが設定できてしまう環境が大多数だからです。詳しい情報は Section 6.11 を見てください。 下記のサブセクションでは、プログラムに対するさまざまな入力について論じ ます。環境変数や umask 値等、プロセスの状態を含む入力には注意が必要です 。入力すべてが信頼できないユーザによってコントロールされているわけでは ないので、これから論じる入力だけを気にかければ OK です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.1. コマンドライン プログラムは、コマンドラインから入力を取ってくる場合がよくあります。 setuid や setgid したプログラムに対するコマンドライン上のデータは、信頼 できないユーザが入力しています。したがって、コマンドラインの値が敵意あ るものである場合に備えて、setuid や setgid したプログラムはプログラム自 身でそれに対処しなければいけません。攻撃者は、ほとんどあらゆる種類のデ ータをコマンドラインから入力できます (execve(3)のようなシステムコールを 呼び出すことで)。したがって、setuid や setgid したプログラムは、コマン ドライン上の入力を完璧に検証し、コマンドラインの引数が 0 番目に当たるプ ログラム名を信頼してはいけません(攻撃者は NULL を含むどんな値も設定でき るため)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2. 環境変数 環境変数は、デフォルトでは親プロセスから継承されます。しかしあるプログ ラムから別のプログラムを実行(exec)した場合、環境変数に任意の値を設定で きます。 setuid や setgid されたプログラムでは、これは危険です。という のも、プログラムを呼び出すことで環境変数のコントロールが可能になり、環 境変数を別のプログラムに渡せてしまうからです。通常、環境変数は継承され てしまうため、この危険性も同時に引き継がれてしまいます。安全性が必要な プログラムが他のプログラムを何も考えずに呼び出すと、もしかすると危険で ある環境変数の値がそのプログラムが呼び出すプログラムに渡されてしまうか もしれません。下記のサブセクションでは、環境変数とその取り扱いについて 論じます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2.1. 環境変数の中には危ないものもある 環境変数の中には危険なものがあります。理由は、たいていの場合ライブラリ やプログラムは環境変数によってコントロールされているものの、その方法が あいまいだったり、わかりにくかったり、ドキュメント化されていないものが あったりするからです。たとえば IFS 変数は、sh や bash でコマンドライン の引数を分割するために使用するキャラクタの指定で利用されています。シェ ルは低レベルのシステムコール(C の system(3) や popen(3)や Perl の backtick 演算子)を利用して呼び出されるため、IFS 変数に異常な値を設定す ると、一見安全と思われるシステムコールを危険なものに変えてしまう恐れが あります。この動作は bash や sh のドキュメントに載っていますが、はっき りとは書いてありません。長年愛用している人だけが、IFS を知っています。 それは IFS を使うとセキュリティが脅かされるという理由であって、本来の目 的で実際によく使われるからではありません。さらに困ったことに、すべての 環境変数がドキュメント化してあるわけではなく、ドキュメント化してあった としても、その他のプログラムが値を変更したり、危険な環境変数を追加して しまう可能性もあります。つまり、唯一の解決方法(下記にあげますが)は、必 要な環境変数を選び出し、残りを捨てることです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2.2. 環境変数の保存方法にまつわる危険性 本来プログラムは、標準的なアクセス手段で環境変数を利用した方がベターで す。たとえば、C では値を取得するのに getenv(3)を使い、値を設定するのに POSIX 規格である putenv(3)を使うか、BSD の拡張である setenv(3)を使いま す。また環境変数を削除するのには、unsetenv(3)を使います。ここで強調して おきたいのは、setenv(3)が Linux でも実装されていることです。 しかし、攻撃者はそのように行儀よくする必要はありません。攻撃者は環境変 数のデータ領域を直接コントロールし、そのデータ領域を execve(2)を使った プログラムに渡せます。これが悪意ある攻撃を可能にしています。この攻撃は 環境変数が実際にどのように動作するかを理解してはじめて理解できる攻撃で す。 Linux では environ(5)を見れば、要約したかたちで環境変数がどのよう に機能するかがわかります。簡単に言えば、環境変数は文字に対するポインタ の配列へのポインタとして記憶しています。この配列は規則正しく並んでいて 、NULL ポインタで終端してあります (したがって、配列の最後がわかります) 。同様に文字へのポインタは、それぞれが NIL で終端してある「名前=値」と いうフォーマットの文字列の値をそれぞれ指しています。これが言わんとする ことは、いくつかあります。たとえば環境変数名には、イコール記号(=)を入れ ることができません。名前だけでなく値にも NIL 文字を埋めこめません。しか し危ないと言う意味では、同じ名前でありながら値が異なる複数のエントリ (たとえば、複数の SHELL 変数値)を認めるというものもあります。代表的なコ マンドシェルは、これを禁止していますが、攻撃者がローカルで作業していれ ば、execve(2)を使ってそのような状況を作れます。 この書式を記録(設定する方法も)する上での問題は、プログラムが (正しい値 であるかを見るために)これらの値の 1 つをチェックすればよいのに、実際は 違うものを使ってしまう点にあります。 Linux では GNU glibc ライブラリが この問題からプログラムを保護することに取り組んでいます。 glibc 2.1 にお ける getenv の実装は、常に最初にマッチした項目を取得し、 setenv と putenv は常に最初にマッチした項目に設定します。unsetenv は、マッチした 項目すべてを解除します(GNU glibc の実装は何と素晴らしいことか!)。しか し、プログラムには環境変数に直接アクセスし、環境変数すべてをなめるもの もあります。この場合は、プログラムが最初ではなく、最後にマッチした項目 を使う可能性があります。となると、最初の項目をチェックしているにもかか わらず、実際は最後の項目を使ってしまうことになります。攻撃者はこの事実 を利用して、保護ルーチンを回避してしまいます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2.3. 解決方法――選別して、消し去る setuid や setgid してあるプログラムを安全にするには、入力として必要な環 境変数(があれば)を減らして、周到に選別しなければいけません。そして環境 変数全体を消し、その後必要となるわずかな数の環境変数に、安全な値を再設 定します。何らかの下位プログラムを呼び出すなら、これが一番優れた方法で す。「危険な値をすべて」をリストアップする手法は、実質的ではありません 。直接間接に呼び出すプログラムごとにソースコードをレビューしたとしても 、あなたがコードを書いた後に、ドキュメント化していない新たな環境変数を 誰かが追加してしまうかもしれません。そういうものの 1 つが危険であるかも しれません。 C や C++ で簡単に環境を消去する方法は、グローバル変数の environ に NULL を設定してしまう方法です。グローバル変数である environ は ; で定義してあり、C や C++ ユーザはこのヘッダーファイルを #include する必 要があります。まずこの値を処理してから、スレッドを立ち上げなければいけ ませんが、それが問題となることはめったにありません。というのは、プログ ラムを実行する際、できるだけ早い段階(通常はスレッドを起こす前)でこれら の処理を行う必要があるからです。 グローバル変数 environ はさまざまな規格で定義してあります。公式な規格で 直接値を変更することを認めているかどうかははっきりしません。しかし直接 値を変更することで問題が発生してしまうような Unix ライクなシステムを私 は知りません。私は通常「environ」だけを直接修正しています。そのような低 レベルの構成要素で処理すると、おそらく互換性はなくなります。しかし確実 にクリーン(で安全)な環境を得ることが保証されます。環境変数全体に対して 、後になってアクセスが必要なケースが時として生じるので、「environ」変数 の値をどこか別のところに保存しておくのもよいでしょう。しかし、プログラ ムにはほんのいくつかの値が必要な場合がほとんどなので、残りは落とすほう がよいでしょう。 もう 1 つ環境をクリアにする方法があります。それは clearenv()というドキ ュメントにのっていない関数を使う方法です。この clearenv()関数は生い立ち が変わっています。POSIX.1 では定義されることになっていましたが、どうい うわけか規格には入りませんでした。しかし、clearenv()は POSIX.9(Fortran 77 の POSIX 規約)で定義しているので、準公式扱いになっています。 Linux で clearenv()は、 で定義してありますが、#include を使って取り 込む前に、必ず __USE_MISC が #defined していなければいけません。もう少 し「公式」なアプローチとして __USE_MISC を定義するのに、まず _SVID_SOURCE もしくは _BSD_SOURCE をまず宣言してから、 #include してください。これらはテスト用マクロとして公式の機能です。 __ PATH は、追記されるタイプの環境変数の 1 つです。ディレクトリのリストに なっていて、プログラムを検索するのに使用します。PATH には、カレントディ レクトリを含めてはいけません。普通は単純に「/bin:/usr/bin」という感じに します。 IFS(デフォルトは「 \t\n」で、最初の文字はスペースです) や TZ (タイムゾーン)も設定しているかもしれません。 Linux は IFS や TZ を設定 していなくても、止まることはありません。しかし、 System V ベースのシス テムの中には、TZ に値を設定しないと問題が起こるものもあります。また、 IFS に値を設定していないとまずいシェルがあるようです。 Linux では environ(5)を見て、一般的な環境変数の一覧を確認し、設定したい変数を見つ けた方がよいでしょう。 ユーザが提供する値を本当に必要とするなら、まず値をチェックしてください (値が正式な値のパタンにマッチしているか、許容している最大文字列長を超え ていないかを確認してください)。 /etc に、「安全な環境変数の基準」を示し た、信頼できる基準となるファイルが存在するのが理想です。しかし現状は、 この目的に合致した基準となるファイルは存在しません。似たようなものとし て、もしシステム上に PAM モジュールがあるなら、pam_env を調べた方がよい でしょう。 シェルをプログラミング言語として使っているなら、「/usr/bin/env」に「-」 オプションを利用します(これで動作するプログラムの環境変数すべてが削除さ れます)。つまり、/usr/bin/env を「-」オプション付きで呼び出して、その後 に変数を準備し、それに値を設定します(名前=値の形で)。次に、プログラムに 引数を与えて起動します。通常はプログラムをフルパス(/usr/bin/env)で指定 してください。ただ「env」としないでください。そうするとユーザが危険な PATH の値を作成してしまいます。 GNU の env には「-i」とその同義である「 --ignore-environment」(これもプログラムが起動すると環境変数を削除する) がありますが、他のバージョンとは互換性がありません。 setuid や setgid するプログラムを作成していて、その開発言語が環境を直接 再設定できないなら、「ラッパー」プログラムを作成するのも手の 1 つです。 ラッパーは、プログラムの環境を安全な値に設定し、他のプログラムを呼び出 します。注意しなければいけないのは、ラッパーが対象となるプログラムを実 際に呼び出さなければいけない点です。そのプログラムがインタプリターなも のなら、競合状態に絶対に陥らないようにしてください。競合状態が起こると 、特別に権限を許可して setuid や setgid してあるプログラムではなく、別 のプログラムをインタプリターがロードしてしまうかもしれないからです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3. ファイル・ディスクリプタ プログラムには「オープンしたファイル・ディスクリプタ」、つまりあらかじ めオープンされているファイルが渡ります。 setuid や setgid されたプログ ラムでは、オープンしているファイルとその内容をユーザが(パーミッションの 範囲内で)切り替えられる、ということに配慮する必要があります。 setuid や setgid されたプログラムでは、新しくオープンしたファイルが常に固定したフ ァイル・ディスクリプタ ID に割り当てられていると想定してはいけません。 また端末が標準入力や標準出力、標準エラーの出力先になっていること、また 端末が既にオープンされていることも前提にしてはいけません。 この理論的根拠は難しくありません。攻撃者がプログラムを起動する前にファ イル・ディスクリプタをオープンしたりクローズしたりできますので、攻撃者 は予想外の状況にしようと思えばできてしまいます。攻撃者が標準出力を閉じ 、その時にプログラムが次のファイルをオープンした時に、あたかも標準出力 がオープンしているかのようになります。そしてプログラムは、すべて標準出 力に書くがごとく、そのファイルに書き込んでしまいます。 C ライブラリの中 には、stdin や stdout、stderr が開いていなければ(/dev/null に対して)、 自動的にオープンするものがあります。しかしこれは Unix ライクなシステム すべてに当てはまるわけではありません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.4. ファイルの内容 ファイルの内容によって、プログラムの動作が左右される場合、信頼できるユ ーザだけがその内容を変更できるのでなければ、そのファイルを信用してはい けません。つまり信頼できないユーザが、ファイルやそのファイルがあるディ レクトリ、その親ディレクトリを修正できてはいけません。そうでないなら、 そのファイルを信頼するに値しないものとして扱わなければいけません。 ファイルに記述してあるやり方が、信頼できないユーザからのものだとするな ら、このドキュメントに書かれている内容にしたがって、そのファイルからの 入力を防いでください。特に正しい値とマッチしているか、バッファがオーバ ーフローしないかを必ずチェックしてください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.5. Web ベースのアプリケーションの入力(特に CGI スクリプト) Web ベースのアプリケーション(CGI スクリプトのような)は、信頼できるサー バ上で稼働し、何らかの方法で Web 経由で入力データを受け取る必要がありま す。入力は概して信頼できないユーザから来ますので、この入力データを検証 する必要があります。実際、情報は信頼できない第三者からやってきます。 Section 6.15 にさらに詳しい情報があります。たとえば、CGI スクリプトは、 情報を標準的な環境変数や標準入力を通じて取得します。このドキュメントの 残りの部分では、CGI にターゲットを当てて論じるつもりです。理由は、CGI が動的に Web コンテンツを実行する最も普及している技術であり、他の動的に Web コンテンツを実行する技術も一般的な問題点は同じだからです。 CGI からの入力の多くが、いわゆる「URL エンコードされた」形式になってい る点が、検証をより厄介にしています。つまり 16 進数の HH というバイト値 を表すには %HH という形式をとります。 CGI や CGI ライブラリは、これらの 入力を適切にデコードして、バイト値が正しいかどうかをチェックする必要が あります。 %00(NIL)や %0A(改行)のような疑わしい値を含むすべての入力を間 違いなく処理しなければいけません。入力のデコードは繰り返し行わないでく ださい。そうしないと、「%2500」のような入力が、誤って処理されてしまいま す(まず %25 が「%」に変換され、その結果「%00」が間違って NIL キャラクタ に変換されてしまいます)。 入力に特殊なキャラクタを混ぜることで、CGI スクリプトを攻撃するケースが まま見られます。上記の解説を見てください。 Web ベースのアプリケーションで扱うデータ形式がもう 1 つあります。それは 、「クッキー」です。このクッキーもユーザが勝手に値を提供できるので、予 防策を特別に取らない限り信頼できません。また、クッキーはユーザを追跡す るのによく利用され、ユーザのプライバシーを侵すかもしれません。結果とし て、ユーザはクッキーを無効にしてしまう場合が多く、Web アプリケーション はクッキーを必要としないように設計した方が良いでしょう(しかし、個々のユ ーザを認証しなければいけないとした以前の議論を見てください)。永続するク ッキー(現在のセッションだけでなく、それ以後も存続するクッキー)の利用を 避けるか、制限をかけることをお薦めします。クッキーは簡単に悪用されてし まうからです。実際現状では、米国の政府機関は永続するクッキーを特別な例 外を除いて禁止しています。ユーザのプライバシー侵害が心配だからです。 OMB guidance in memorandum M-00-13 (June 22, 2000) を見てください。クッキーを使用する上で注意し なければいけないのは、ブラウザの中にはプライバシー・プロファイル(サーバ のルートディレクトリにある p3p.xml がそれです)を必要とするものがあるか もしれません。 HTML のフォームにはクライアント側での入力チェックを入れて、これで不正な 値を防御するものがあります。これらは普通、Javascript や ECMAscript、 Java で実装してあります。このチェックは、ユーザにとっては役に立ちます。 ネットワーク経由でアクセスしなくても、「すぐに」チェックができるからで す。しかし、この種の入力チェックは、セキュリティの点からすると無駄なチ ェックです。理由は、攻撃者は「不正な」値をチェックを受けずに直接 Web サ ーバに送りつけられるからです。このチェックを駄目にするのでさえ、難しい ことではありません。Web アプリケーションに対して、任意のデータを送るよ うなプログラムを書く必要はありません。一般的には、サーバは入力チェック をすべて自前で行う必要があります(フォームのデータやクッキー等)。サーバ は、クライアントがしっかりしているとは信じられないからです。つまり、ク ライアントは一般的に「信頼に足る伝達経路」ではないからです。信頼できる 伝達経路については、Section 6.11 にさらに情報があります。 Microsoft の Active Server Pages(ASP)を使って入力の妥当性を確認する議論 については、Jerry Connolly 氏が http://heap.nologin.net/aspsec.html で 簡潔に論じています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.6. その他の入力 プログラムでは、必ず入力すべてをコントロールしてください。しかし setuid や setgid されたプログラムでは困難を極めます。理由は、そのような入力が あまりに多いからです。その他の入力プログラムでは、下記の点を考慮する必 要があります。それはカレントディレクトリやシグナル、メモリ・マップ (mmap)、System V 由来の IPC、 umask(新規にファイルを作成する場合のデフ ォルトのパーミッションを決定する) についてです。プログラムを起動する時 、ディレクトリを(chdir(2)を使用して)変更する場合は、フルパス指定できち んと目的のディレクトリに移動することも考慮してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7. 自然言語(ロカール)の選択 コンピュータが増加し、インターネットが身近になるにつれて、プログラムで 複数の言語や文化をサポートすることが強く求められてきています。言語とそ の他の文化に関連した要素のことを普通「ロカール(locale)」と言います。複 数ロカールに対応するためプログラム修正する過程を「国際化 (internationalization)」(i18n)と呼び、特定のロカール情報をプログラムに 提供することを「地域化(localization)」(l10n)と言います。 全般的には国際化は良いことですが、この過程でセキュリティを侵害する機会 がさらに追加されます。信頼できないユーザが、望ましいロカール情報を提供 できてしまえます。つまり、ロカールを選択する際に、指定したものと異なる ロカールを入力してしまえます。きちんと防御していなければ、これが悪用さ れてしまう可能性があります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7.1. ロカールを選択するには ローカルで起動されるプログラム(setuid や setgid したプログラムを含む)で は、環境変数がロカール情報を提供します。つまり他の環境変数すべてと同じ ように、使用する前に選択してから、正しいパタンに反していないかチェック しなければなりません。 Web アプリケーションは、この情報を Web ブラウザから入手します (Accept-Language 要求ヘッダ経由で)。しかし、ブラウザがすべて正確にこの 情報を渡してくるわけではないので (ユーザすべてがブラウザを正しく設定し ているわけではないので)、思っているほど役に立ちません。 Web ブラウザが 言語を要求する場合、たいていはただフォームの値として渡すだけです。つま り、他のフォームの値と同様に、これらの値は使う前に正しいかどうかチェッ クしなければいけません。 ロカール情報は、どちらのケースにおいても、先のセクションで議論した入力 という意味でまさに特殊なケースです。しかし、この入力はほとんど考慮され ていないので、あえて独立して論じました。特に書式文字列(後で論じます)と 組み合わさると、ユーザが管理している文字列によって他のプログラムで任意 の命令や不正なデータを動かしたり、その他不適切な動作を攻撃者が実行でき たりします。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7.2. ロカールを動かすメカニズム ロカール・メッセージを選択する方法として、Unix ライクなシステムには大き く言って 2 つのライブラリ・インタフェースがあります。1 つは「catgets」 、もう 1 つは「gettext」です。 catgets のアプローチは、すべての文字列は ユニークな番号が割り振られていて、その番号をメッセージが書いてあるテー ブルのインデックスとして使っています。一方 gettext のアプローチでは、文 字列(通常は英語)を使って、テーブルにある文字列を翻訳したものを捜します 。 catgets(3) は規格として認められていて(X/Open Portability Guide の 3 号と Single Unix Specification で)、プログラムで利用可能です。「gettext 」のインタフェースは、公式の規格ではありませんが(しかしもともとは UniForum の提案でした)、インタフェースとして catgets より利用されている と思っています(Sun や GNU のすべてのプログラムで)。【訳註:UniForum に ついては、 http://www.uniforum.org/ を見てください】 原理的には catgets の方がわずかに速いはずですが、最近のマシンであればそ の差はほんのわずかです。また、catgets() が一意の識別子を維持・管理する のが面倒で、gettext() のインタフェースの方が使いやすくなっています。私 としては、gettext()を使用することをお薦めします。これは使いやすいからに 他なりません。しかし私の言葉をそのまま鵜呑みにしないでください。gettext については GNU のドキュメント(info:gettext#catgets) で、たっぷりいろい ろと比較していますので、見てください。 catgets(3)(とそれと関連している catopen(3))はセキュリティ上の問題に対し てとても脆弱です。それは環境変数である NLSPATH を使用して、国際化された メッセージを取得するファイル名を管理しているからです。 GNU C ライブラリ は NLSPATH を setuid や setgid したプログラムでは無視するようになってい ます。これは役には立ちますが、他の実装で動作するプログラムを防御できま せんし、そのような防御が必要とは「見えない」その他のプログラム(CGI スク リプトのような)も防御できません。 広く利用されている「gettext」のインタフェースは、少なくとも私の知る限り 、悪意を持って設定した NLSPATH に対して脆弱ではありません。しかし、悪意 を持って設定した LC_ALL や LC_MESSAGES は、問題を起こすように思えます。 また、gettext の cat-compat.c にある bindtextdomain() ルーチンを使うと NLSPATH に頼ることになります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7.3. 正しい値 とりあえず、信頼できないユーザに希望するロカールを設定させるなら、設定 しようとする国際化情報がフィルタに必ず合致するようにしてください。この フィルタでは、正しいロカールの名前だけを許可するように限定しておきます 。ユーザ・プログラム(特に setgid や setuid してあるプログラム)は、これ らの値を次の変数から取得します。それは、NLSPATH や LANGUAGE、LANG、古く なった LINGUAS、LC_ALL、その他の LC_* (LC_MESSAGES だけでなく、 LC_COLLATE、 LC_CTYPE、LC_MONETARY、LC_NUMERIC、LC_TIME も)です。 Web アプリケーションでは、ユーザが要求する言語情報は Accept-Language 要求ヘ ッダもしくはフォームの値として提供されます(アプリケーションは、 Content-Language: ヘッダーを使って、返されるデータの実際の言語設定を示 すべきです)。ユーザがあなたの環境変数を設定していれば(つまり、setgid や setuid してあるプログラム)、環境変数のフィルタリングの一部もしくは、入 力フィルタ(たとえば CGI スクリプト用に)の一部としてこの値をチェックでき ます。 GNU の C ライブラリである「glibc」は、setgid や setuid したプロ グラムでは LANG の値を受け付けないものがありますが(特に「/」を伴ったも の)、そのフィルタにはエラーがあることがわかっています(たとえば、Red Hat はこのエラーを修正するために、glibc のアップデートを 2000 年 9 月 1 日 にしています)。この種のフィルタリングは規格上必要とはされていませんので 、あなた自身がフィルタリングを行なうことで、より安全にできます。フィル タリング言語の設定については、手引きが何も見つけられませんでした。そこ でここでは、この件について私自身が調査したことに基づいて、アドバイスを します。 まずは、これらの設定で何が正しい値かについて一言述べておきます。言語設 定は、一般的に IETF RFC 1766 で定義している標準タグを使っています (2 文 字の国コードを基本タグとし、その後に任意でダッシュ(-)で区切ったサブタグ が続くことがあります。環境変数の場合、アンダースコアを代わりに使います) 。しかし、これは柔軟であるとは言い難く、3 文字の国コードがまもなく利用 できるようになるでしょう。また、機能を拡張したメジャーな 2 つのフォーマ ットがありますが、互換性があるとはいえません。それは X/Open フォーマッ トと CEN フォーマット(European Community Standard)です。どちらも許可し て良いでしょう。典型的な値としては、「C」(C ロカール)や「EN」(英語)、「 FR_fr」(フランスの慣習が生きている地域で利用しているフランス語)がありま す。また標準ではない名称を使っている場合が多く、プログラムは「別名 (alias)」を使える仕組みを開発する必要にせまられ、標準ではない名称を扱え るようになりました(GNU の gettext なら /usr/share/locale/locale.alias、 X11 なら /usr/lib/X11/locale/locale.alias を見てください。「alias」では なく、「aliases 」とする必要があるかもしれません)。どちらも普通は利用で きるはずです。 gettext()のようなライブラリは、これらのエイリアスをすべ て受け付けなければならず、できるだけ適切な値を適用できなければいけませ ん。より詳しい情報は、FSF [1999]や li18nux.org の Web サイトにあります 。フィルタは、不必要な文字を許可すべきではありません。特に「/」(信頼さ れているディレクトリから抜け出てしまえる可能性がある)や「..」(上位ディ レクトリに移動できてしまう可能性がある)は許可してはいけません。 NLSPATH に含まれる他の危険な文字には、「%」(置換を表わす)と「:」(ディレクトリの 区切り)があります。私が所有している他のマシン用資料によると、実装によっ て、これらの文字が他の値を示すために使われている場合もありますので、禁 止した方が安全である、となっています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7.4. 結論 つまり私としては、NLSPATH を削除するか、再設定するかのどちらかを推奨し ます。そうしないと、その値を渡してくるユーザを信頼しなければいけなくな ります。 HTTP における Accept-Language ヘッダ(を使うなら)や、ロカールを 指定するフォームの値、上記で挙げた環境変数の LANGUAGE や LANG、古い LINGUAS、 LC_ALL、その他の LC_* に対しては、信頼できないユーザからのロ カールに null (値なし)を設定するか、正規表現全体にマッチした値だけを許 可するようにフィルタをかけてください(私は最近このフィルタに「=」を追加 しました)。 [A-Za-z][A-Za-z0-9_,+@\-\.=]* 正しいロカールで、このパタンにマッチしないものを見たことがありませんが 、このパタンで、ロカールを利用した攻撃を防ぐようです。もちろん、要求さ れたロカール中に利用できるメッセージが存在する保証はありません。しかし その場合でも、これらのルーチンはデフォルトのメッセージ(通常は英語) を表 示します。これがセキュリティ上問題とはならないのは確かです。 本当にこだわるなら、代わりに li18nux で提供しているロカールのパタンにマ ッチするものだけを使ってください。 ^[A-Za-z]+(_[A-Za-z]+)? (\.[A-Z]+(\-[A-Z0-9]+)*)? (\@[A-Za-z0-9]+(\=[A-Za-z0-9\-]+) (,[A-Za-z0-9]+(\=[A-Za-z0-9\-]+))*)?$ どちらの場合も、POSIX の拡張(「新しい」)正規表現の考えに基づいています (Unix ライクなシステムなら regex(3) や regex(7)を見てください)。 もちろん言語というものは、標準的な手段で書き文字を表現できなくては、言 語をサポートしているとは言えません。このことから文字のエンコードという 問題に直面することになります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8. 文字のエンコード 4.8.1. 文字のエンコードとは 長年に渡って、米国では ASCII 文字セットを使って文字のやり取りをしてきま した。米国のシステムは基本的に ASCII をサポートしているので、簡単に英語 の文字でやり取りできます。残念なことに、その他言語の文字の大半を扱うに は ASCII ではまったく力不足です。これまでずっと、さまざまな国でいろいろ なテクニックを使って、さまざまな言語で文字をやり取りしてきました。この ことが、ますます相互につながりを持ちつつある世界で、データをやり取りす ることを困難にしています。 つい最近、ISO は ISO 10646 を整備し、「Universal Mulitple-Octet Coded Character Set(UCS)」としました。 UCS は全世界の文字それぞれに対して、31 ビットの値を定義した符号化文字集合です。 UCS のはじめから 65536 文字(16 ビットに当たります)は、「Basic Multilingual Plane(BMP)」とし、今日使用 されている言語をほぼカバーすることを目的としています。 Unicode コンソシ アムは Unicode 規格を作成しました。これは UCS に焦点を当て、追加でいく つか規約を設け、共同で運用できるようにしています。もともと Unicode と ISO 10646 は競いあって制定を進めてきましたが、ありがたいことに共同して 作業をする必要があることを理解し、今ではお互いに連携しています。 多言語を扱う新規のソフトウェアを書くなら、ISO 10646 や Unicode を基本と して多言語を扱うようにしてください。しかし、さまざまな(言語固有の)文字 集合で書かれた古いドキュメントを処理する必要があるかもしれませんので、 信頼できないユーザが他のドキュメントの文字集合をコントロールできないこ とを必ず確認してください。 (ドキュメントの変換処理に影響が大きいからで す)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8.2. UTF-8 とは ソフトウェアの大半は、16 ビットや 32 ビットの文字を扱うように設計してお らず、 8 ビット以上が必要な多言語文字集合を作成してもいません。そのため UTF-8 という特別なフォーマットが開発され、既存のプログラムやライブラリ が、多言語を表現できる文字列を容易に扱えるような形式にエンコードするこ ととなりました。 UTF-8 は IETF RFC 2279 で定義してありますので、よくま とめられた規格が自由に読めて利用ができるのは幸いなことです。 UTF-8 は可 変長でエンコードします。0 から 0x7f(127)の文字は 1 バイトでそのままエン コードしますが、それより大きな値の文字は 2 から 6 バイトの情報としてエ ンコードします(値によってバイト数がかわります)。エンコードは、下記の属 性に適合するように特別に設計してあります(これは RFC もしくは Linux に含 まれている utf-8 の man からの情報です)。 ・ これまで利用していた US ASCII 文字(0 から 0x7f)はそのままエンコード しますので、 7 ビットの ASCII 文字だけのファイルや文字列は、ASCII でも UTF-8 でも同じエンコードを行います。この方法は、多量にある既存 の米国製プログラムやデータファイルにとって下位互換性という点で優れ ています。 ・ 0x7f より大きい UCS 文字すべてはマルチバイト文字列としてエンコード し、 0x80 から 0xfd の範囲に納めます。つまりASCII 文字を他の文字の 一部として表現することはありません。他のエンコード方法では NIL のよ うな文字を組み込めるので、プログラムが処理できなくなってしまいます 。 ・ UTF-8 と 2 バイトもしくは 4 バイト固定長の文字表現間の変換は簡単に できます(それぞれ UCS-2 及び UCS-4 と呼ばれます)。 ・ UCS-4 文字列での辞書順ソートの並びはそのままなので、Boyer-Moore 法 による高速検索アルゴリズムが UTF-8 のデータにも直接利用できます。 ・ 2^31 ビットのすべての UCS コードが UTF-8 を使用してエンコードできま す。 ・ あるマルチバイト文字列の先頭のバイトが ASCII 文字ではない場合、その 値の範囲は常に 0xc0 から 0xfd になり、そのマルチバイト文字列長がど のくらいなのかを示しています。残りすべては 0x80 から 0xbf の範囲に なります。これで簡単に同期を取り直せます。つまりあるバイトが落ちて しまっても、スキップすることで簡単に「次」の文字に進めますし、「前 後」の文字にも簡単に行きつ戻りつできます。 要するに UTF-8 の変換フォーマットは、多言語のテキスト情報をやりとりする のに秀でており、世界中のあらゆる言語をサポートできます。その上なお、US ASCII ファイルと下位互換性があると同時に、他の優れた属性も持っています 。いろいろな目的に採用することをお薦めします。「テキスト」ファイルにデ ータを保存する場合にはなおさら。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8.3. UTF-8 のセキュリティ上の課題 UTF-8 に言及する訳は、バイト列に不正な UTF-8 があると、それがセキュリテ ィホールになるかもしれないからです。 UTF-8 は、「最短」エンコードの利用 を想定していますので、そのままデコードすると、必要以上に長い文字列をエ ンコードしたものを受けてしまうかもしれないからです。実際、初期の規格で は「最短」でないエンコードを認めていました。ここで問題となるのは、複数 の方法で危険な入力が行われる可能性があり、そのことによって、危険な入力 に対するセキュリティ処理が無になるかもしれないということです。 RFC では その問題を下記のように記載しています。 UTF-8 の実装では、不正な UTF-8 文字列をどのように対処するか、という セキュリティ上の観点を考慮する必要があります。環境によっては、攻撃 者が無防備な UTF-8 パーサに UTF-8 の文法では認められていないオクテ ット文字列を送りこみ、悪用してしまうことも考えられます。 この攻撃は、入力に対してセキュリティに重点をおいた正当性チェックを 行うパーサに対して、実に巧妙に実行されます。UTF-8 でエンコードして あるものの、文字として不正なオクテット列として解釈されてしまう入力 がそれに当たります。たとえば、パーサは 00 という単独のオクテット文 字がエンコードされた場合には NULL 文字を禁止しているかもしれません 。しかし不正であるオクテット文字 2 文字である C0 80(必要以上に長い) は許しており、それを NUL 文字(00)として処理しています。その他の例と しては、オクテット文字列である 2F 2E 2E 2F ("/../")は禁止しています が、不正である 2F C0 AE 2E 2F は許してしまっています。 この件についてのさらなる論議は Markus Kuhn 氏のサイト http:// www.cl.cam.ac.uk/~mgk25/unicode.html にある UTF-8 and Unicode FAQ for Unix/Linux で読めます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8.4. UTF-8 の正しい値 つまり、UTF-8 を入力として受け付ける場合は、その入力が正しい UTF-8 なの かをチェックする必要があります。ここで挙げる一覧は、正しい UTF-8 文字列 すべてです。文字がこのテーブルにマッチしなければ、正しいとは言えません 。下記のテーブルで 1 番目のカラムは UTF-8 にエンコードする各種文字コー ドです。 2 番目は文字がどのようにバイナリにエンコードするかを示していま す。「x」はデータがあること(0 か 1)を示しますが、最短エンコードでない場 合には認めるべきでないケースもあります。最後は、それぞれのバイトが取り うる正しい値(16 進表示)です。したがって、個々の文字が右側のカラムのパタ ンのどれに当てはまるのか、プログラムでチェックする必要があります。「-」 は正しい値の範囲(両端を含む)を表わしています。もちろん、文字列が正しい UTF-8 の文字列であると言うことだけで、受け入れて良いとは言えませんが(そ の他のチェックも必要)、普通、他のチェックをする前に UTF-8 の正当性をチ ェックする必要があります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Table 4-1. Legal UTF-8 Sequences ┌──────────┬───────────┬──────────┐ │UCS Code (Hex) │Binary UTF-8 Format │Legal UTF-8 Values │ │ │ │(Hex) │ ├──────────┼───────────┼──────────┤ │00-7F │0xxxxxxx │00-7F │ ├──────────┼───────────┼──────────┤ │80-7FF │110xxxxx 10xxxxxx │C2-DF 80-BF │ ├──────────┼───────────┼──────────┤ │800-FFF │1110xxxx 10xxxxxx │E0 A0*-BF 80-BF │ │ │10xxxxxx │ │ ├──────────┼───────────┼──────────┤ │1000-FFFF │1110xxxx 10xxxxxx │E1-EF 80-BF 80-BF │ │ │10xxxxxx │ │ ├──────────┼───────────┼──────────┤ │10000-3FFFF │11110xxx 10xxxxxx │F0 90*-BF 80-BF │ │ │10xxxxxx 10xxxxxx │80-BF │ ├──────────┼───────────┼──────────┤ │40000-FFFFFF │11110xxx 10xxxxxx │F1-F3 80-BF 80-BF │ │ │10xxxxxx 10xxxxxx │80-BF │ ├──────────┼───────────┼──────────┤ │40000-FFFFFF │11110xxx 10xxxxxx │F1-F3 80-BF 80-BF │ │ │10xxxxxx 10xxxxxx │80-BF │ ├──────────┼───────────┼──────────┤ │100000-10FFFFF │11110xxx 10xxxxxx │F4 80-8F* 80-BF │ │ │10xxxxxx 10xxxxxx │80-BF │ ├──────────┼───────────┼──────────┤ │200000-3FFFFFF │111110xx 10xxxxxx │too large; see below│ │ │10xxxxxx 10xxxxxx │ │ │ │10xxxxxx │ │ ├──────────┼───────────┼──────────┤ │04000000-7FFFFFFF │1111110x 10xxxxxx │too large; see below│ │ │10xxxxxx 10xxxxxx │ │ │ │10xxxxxx 10xxxxxx │ │ └──────────┴───────────┴──────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 先に触れたように、文字集合には ISO 10646、Unicode という 2 つの規格があ りますが、文字の割り当てに関しては同期を取っています。現状、ISO/IEC 10646-1:2000 と IETF RFC における UTF-8 の定義では、5、6 バイト文字列の エンコードもサポートしており、文字を Uniforum の Unicode の範囲外にエン コードしています。しかしそのような値は、Unicode 文字としてはサポートさ れてこなかったので、ISO 10646 の将来のバージョンでも同様の制約を受ける と思われます。つまり、5、6 バイトの UTF-8 のエンコードが正しいケースは ほとんど無く、通常は拒否しなければいけません(特別な目的が無い限り)。 正しい値の範囲を特定するのは困難です。そして実際このドキュメントの初期 の版では、間違った項目がいくつか記載してありました(長すぎる文字を許して しまっているケースがありました)。言語開発者は、ライブラリに正しい UTF-8 の値をチェックする機能を入れる必要があります。チェックを正しく行うのは とても困難なので。 場合によって、16 進の C0 80 に対して、それほどシビアにしたくない(もしく は内部で何とかしたい)ケースがあるかもしれない、という点を示しておきます 。これは長すぎる文字列で、許可してしまうと ASCII の NUL(NIL)に相当する ことになります。C と C++ では NIL 文字を通常の文字列に入れてしまうとや っかいなことになりますので、データストリームの一部として NIL を表したい 時に、この並びを用いるケースがあります。Java ではこの操作を正式に記載し ています。データ処理を行う時に、内部的には C0 80 を好きに扱ってください 。ただし、厳密に言うと、そのデータを保存する前に 00 に変換し直す必要が あります。必要性にもよりますが、「シビアにならずに」、C0 80 を UTF-8 の データストリームとして認めてもよいかもしれません。セキュリティに影響が でないなら、運用を助けるという観点で許可するのはよい案だと思います。 この対処は微妙です。 Unicode フォーラムで開発した C による変換ルーチン を調査したいなら、 ftp://ftp.unicode.org/Public/PROGRAMS/CVTUTF/ ConvertUTF.c を利用してみてはどうでしょうか。このルーチンがオープンソー スかどうかはっきりしませんので(ライセンスを読んでも、修正可能かどうかわ かりません)、その点は注意してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8.5. UTF-8 関連について このセクションでは UTF-8 を論じます。UCS で多バイトをエンコードするのに 最も一般的で、各種の国際化テキストが抱える問題を簡単に扱えるからです。 しかし、それだけがエンコードでないのも事実です。他のエンコードとして UTF-16 や UTF-7 のようなエンコードがあり、同様な問題を抱えていますので 、同じような理由で検証しなければいけません。 もう 1 つの問題として、複数の表現法で表せるものが ISO 10646 や Unicode にある点です。たとえば、アクセント文字の中には 1 文字(アクセント付き)で 表現できるものがありますが、文字の組み合わせ(たとえば、ベースになる文字 にアクセントをのせる) で表現できるものもあります。この 2 つの形式は、同 一であるかもしれません。幅がないスペースを挿入した結果、異なるものが見 た目同じように見えることもあります。そのような隠れたテキストが存在する 状況において、プログラムに影響が出る点に注意を払ってください。これは一 筋縄では行かない問題です。プログラムは、特定の文字列をどのように表示す るのかを完全に掌握しているクライアントに対して、そのようなきつい制約を かけていない場合が大半です(クライアントのフォントや表示特性、ロカール等 に依存しているので)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.9. サイトにまたがった悪意あるコンテンツ(Cross-site Malicious Content) を防ぐ 信頼できないユーザからのデータを受け付けて、そのデータを次のユーザに渡 すプログラムもあります。二番目のユーザのアプリケーションが、見方によっ てはそのユーザにとって迷惑になる処理をするかもしれません。これは Web ア プリケーションではありがちな問題です。この問題をサイトにまたがった悪意 あるコンテンツ(Cross-site Malicious Content)と呼ぶことにします。つまり 、入力(フォームデータも含む)を必ずチェックして、フィルタをかけるか、エ ンコードするかしないといけません。詳しい情報は、Section 6.15 を見てくだ さい。 原則として、Web アプリケーションへの入力はすべて、フィルタをかけたり(こ の問題を起こす文字を削除する)、エンコードしたり(この問題を起こす文字が 問題を起こさないようにエンコードする)、検証したり(確実に「安全な」デー タだけが通過するようにする)しなければいけないことを意味します。フィルタ リングや検証は、入力時に終わらせた方が良いのが普通ですが、エンコードは 入力時でも出力時でも済ませられます。分析をせずにデータを通してしまうな ら、入力時にデータをエンコードする方が良いと思います(忘れないでしょうか ら)。しかしデータを処理しているなら、エンコードを入力時ではなく出力時に する点については、まだどちらがよいか結論が出ていません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.10. 再表示する可能性のある HTML や URI にはフィルタをかける サイトにまたがった悪意あるコンテンツ(Cross-site Malicious Content)を防 がなければならない特徴的なケースの 1 つに、Web アプリケーションが挙げら れます。その Web アプリケーションは、あるユーザから HTML や XHTML を受 け取り、それを他のユーザに渡すように設計してあります(詳しい情報は Section 6.15 を見てください)。下記のサブセクションでは、特にこの種の入 力のフィルタリングについて論じます。そういうケースを扱う必要性が当たり 前になってきたからです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.10.1. HTML データを削除したり禁じたりする (X)HTML タグをできるだけ削除すれば、最も安全になります。そうすれば、タ グによる影響は何も起こらず、また実現するのも比較的簡単です。以前指摘し たように、正しい文字の一覧を確認しているはずですから、その一覧にない文 字を拒否したり、削除したりできるはずです。正しい文字の一覧に載っている からといって、単純にこのフィルタへ次の文字を入れてはいけません。その文 字とは「<」や「>」、「&」(属性に使うなら二重引用符の「"」も)です。ブラ ウザが HTML の仕様に従って動作するだけなら、「>」は削除する必要はありま せん。しかし、実際は削除しなければいけません。理由は、開始を示す「<」を そのページの著者が本当は置きたかった、と推測しているブラウザがあるから です。この「手助け」が、攻撃者につけ込む余地を与えて、「>」を使って「 <」という望ましくない文字を作ってしまいます。 文字集合を HTML で送るには、通常 ISO-8859-1(国際化テキストを送る時でさ え) を使います。したがってフィルタは制御文字(改行やタブは普通は OK)のほ とんどとハイビットにある文字も削除するべきです。 このやり方で問題になるものの 1 つは、国際化テキストを入力したユーザがそ のテキストが知らない内に消されてしまい、びっくりするという点です。無効 な文字が何の警告もなく削除されると、そのデータは完全になくなり、後にな って再構成のしようがありません。選択肢の 1 つとして、そのような文字を禁 止した上で、文字を使おうとしたユーザにエラーメッセージを送り返してあげ る方法があります。少なくともこれでユーザに警告を出せますが、ユーザが望 んでいる機能を提供できるわけではありません。その他には、そのデータをエ ンコードする方法と検証する方法が挙げられます。これについては次に議論し ます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.10.2. HTML データをエンコードする 他にほぼ安全な方法として、危険な文字を変換してしまい、HTML 上で意味を無 くす、というものがあります。すべての「<」を「<」に、「>」を「>」 に、「&」を「&」にしてしまえばお終いです。国際化文字ならどれも「&# value;」という形式を使って、Latin-1 にエンコードできます。最後のセミコ ロンを忘れないでください。当然、入力エンコードをどうするかを理解してい なければいければ、国際化文字のエンコードはできません。 ここで考えられる危険には、エンコードした結果をたまたま 2 回処理すると脆 弱さが生まれてしまう、という現象が挙げられます。しかしこのやり方では、 少なくとも入力の「目的」が何であるのかを受けとったユーザに伝えられます 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.10.3. HTML データを検証する アプリケーションがすべて機能する過程で、HTML を第三者から受け付けなけれ ばならず、その受け付けた内容を別のユーザに対して送る場合があります。こ れは用心しなければいけません。今あなたは、とても危ない橋を渡っています 。本当にこうすることが必要なのか、自問自答してください。あらゆるところ から HTML を受け入れる、という考えでさえ、セキュリティに精通した人々の 間では賛否両論です。なぜなら、正しく取得するのは極めて困難だからです。 しかし、アプリケーションで HTML を受けざるを得ず、リスクを負うだけの価 値があると思うのなら、少なくとも HTML の「安全な」コマンドの一覧を確認 してから、そのコマンドだけを許可するようにしてください。 安全な HTML タグでアプリケーション(ゲストブックのような)にとって役に立 つものを最低限ここにあげました。簡単なコメントをつけてあります。

(パラグラフ)、 (ボールド)、 (イタリック)、 (強調)、 (さらに強調)、

 (事前に整形してあるテキスト), 
(強制 改行。閉じ用のタグは必要ありません) 上記に対応して終了タグもあります。 少数の「安全な」HTML コマンド群だけを受け入れるだけではなく、それらが入 れ子になって閉じている(つまり、HTML コマンドが「対応がとれている」) よ うに必ずしてください。 XML では、これを「整形式(well-formed)」データと 呼んでいます。標準 HTML を許しているなら、多少例外があるでしょう(たとえ ば、

が出てくる前のところに

を想定するのは問題ないと思います)。 しかし、HTML ができる表現すべて(対応をとるための閉じ用タグが推測できる 場合が多い)を受け入れようとするのは、アプリケーション大半にとって必要で はありません。もっとはっきり言うと、XHTML(HTML のかわりに) に忠実であろ うとするなら、整形が必要条件です。また、HTML タグは大文字、小文字を区別 しません。タグは大文字でも、小文字でも、混ぜて使ってもかまいません。し かし、XHTML を受け入れるつもりなら、タグはすべて小文字にしなければいけ ません(XML は大文字、小文字を区別します。XHTML は XML を使い、タグが小 文字であることが必要です)。 ここでいくつか TIPS を順不同であげておきます。通常は、HTML テキスト及び 許可すべきタグの集合に関する何かしらの設計を行なった方が良いでしょう。 そうすれば、投稿されたテキストが「メイン」サイトのテキストとして誤って 処理されなくなります(偽造を防ぎます)。どんな属性も、その属性タイプや値 をチェックすることなしに受け入れてはいけません。 Javascript のように、 ユーザをトラブルに巻き込む恐れがある属性がたくさんあります。それらの属 性をサポートする必要があります。上記の一覧には、属性がまったく存在しな いことに注目してください。これが安全、確実な方法なのです。安全ではない タグが使われたなら、おそらく警告メッセージを出した方が良いでしょう。し かしこれが現実的でないなら、危険な文字をエンコードして(たとえば「<」を 「<」にする)、ユーザの安全を維持しつつ、データがなくなることは防いで ください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.10.4. ハイパーテキストリンク(URI や URL)を検証する 注意深い方ならお気づきだと思いますが、ハイパーテキストのリンクタグ を安全な HTML タグとはしていません。明らかに、(ハイ パーテキストリンク)を安全な一覧に追加できたのにもかかわらずです(内容を チェックしない限り、他のどの属性も許可しないこと)。アプリケーションが必 要としているなら、追加してもかまいません。しかし第三者がリンクを張るこ とで、安全性がさらに低下します。理由は、「安全な URI」の定義にあります 。[1] これが結果的にはとても面倒事になります。多くのブラウザは、ユーザ にとって危険かもしれないあらゆる URI をすべて受け取ってしまうからです。 このセクションでは、第三者からやって来て、他の人に再表示する URI の検証 方法と、その URI を HTML にどのように組み込むかについて論じます。 まず URI の文法をざっと見て行きましょう(さまざまな仕様書で定義してある ので)。 URI は「絶対」も「相対」も可能です。絶対 URI はこのようになりま す。 scheme://authority[path][?query][#fragment] URI はスキーム名(「http」のような)からはじまり、「://」、責任者部 (authority)、 (「www.dwheeler.com」のように)、パス(ディレクトリ名やファ イル名のような) と続きます。この後に疑問符を置いてクエリが続いたり、ハ ッシュ(「#」)を置いてフラグメント識別子が続いたりします。オプション部分 は [] で囲んであります。ただ現実には、クエリやフラグメントを含む URI は 多くはありません。スキームには、許可しないデータ(たとえばパスやクエリ、 フラグメント)がある一方、固有の条件を追加する場合もたくさんあります。ス キームは「責任者部」にオプションでユーザ名やパスワード、ポート番号を許 可している場合がよくあります。書き方は次の通りです。 [username[:password]@]host[:portnumber] 「host」は名前(「www.dwheeler.com」)か IPv4 の数値形式のアドレス (127.0.0.1) を指定できます。「相対」URI はあるオブジェクトを「現在の」 オブジェクトからの相対位置で参照し、その書き方はファイル名にとてもよく 似ています。 path[?query][#fragment] たいていの URI では、許可している文字数に制限があり、この問題を回避する ために 8 ビット文字を「URL エンコード」して %hh(hh には 8 ビット文字が 16 進の値で入ります) とします。正しい URI について、さらに詳しい情報は IETF RFC 2396 とそれに関連した規格書を見てください。 ここまで URI の書き方を見てきましたので、こんどはそれぞれの部分が持つ危 険性を調べてみましょう。 ・ スキーム:スキームの大部分は実に危険です。「javascript」を含むスキ ームを挿入できるようにしてしまうと、ちょっとしたサービス拒否攻撃を 実装することになってしまいます(たとえば、ウインドウ作成を繰り返すこ とで、ユーザのマシンがフリーズして、利用できなくなります)。もっと深 刻なのは、javascript の実装にある既知の脆弱性を攻撃される恐れがある 点です。スキームには「mailto:」のように、メールを出すつもりがなくて も出してしまう困ったものや、クライアントマシンで十分に安全が確保で きないものもあります。つまり、少数の安全なスキームだけに限定して、 スキームを許可する必要があります。 ・ 責任者部:欲を言えば、ユーザには「安全な」サイトへのリンクだけを許 可した方が良いでしょう。しかしそれは現実的にとても困難です。しかし 、ユーザ名やパスワード、ポート番号に対して何か手を打たなければいけ ません。禁止すべきなのです。ユーザ名(とりわけパスワードをともなっ た)を必要としているシステムはおそらくより重要な情報をガードしていま す。誰でもポストできる URI では、ほとんどこの機能は必要ではありませ ん。またユーザがアクセスした情報を見せたり、ユーザが情報を修正した りするのをユーザに分かってもらうために、この機能を利用しているとこ ろもあります。このような URI では、セマンティック攻撃(semantic attack)が可能になります。詳しくは、Section 6.16 を見て下さい。パス ワード無しのユーザ名もやはり危険です。ブラウザはたいてい、パスワー ドをキャッシュするからです。通常は、ポート指定はすべきではありませ ん。理由は、ポートが異なれば、プロトコルも異なることを期待して、結 果的に「プロトコルの混乱」で攻撃の隙を与えるからです。たとえば、あ るシステムでは「gopher」スキームが利用可能で、SMTP(電子メール) ポー トを指定することで、あるユーザに攻撃者が送りたいメールを送らせられ ます。特殊なケース(たとえば http ポートを 8008 や 8080 とする)は認 めてもよいかもしれませんが、全体的に見ると、そうするだけの価値はあ りません。ホストを名前で指定する場合に、かなり文字集合に制限があり ます(DNS の仕様を使うと)。技術的に言うと、仕様ではアンダースコア(「 _」)を認めていませんが、Microsoft はこの仕様の部分を無視しているだ けでなく、ある環境ではアンダースコアの使用を必要とさえしています。 したがっておそらく認めざるを得ないケースがあるでしょう。また DNS 名 で多言語をサポートする作業に精力をつぎ込んでいますが、ここではこれ 以上は論じません。 ・ パス:普通、パスを許可して問題ありませんが、残念ながらパスの一部を クエリとして使って、穴を空けてしまうアプリケーションがあります。こ れについては次で論じます。また、パスに「..」と同種の書式を設定でき ますので、いい加減な実装をしている Web サーバでは、プライベートなデ ータをさらしかねません。これは、以前ほど問題ではなくなっているので 、Web サーバ側できちんと修正してください。「..」という書式だけは特 別で、パス(できればクエリも)見て、「../」を設定できないようにしてく ださい。しかし、検証する仕組みが URL エスケープを許可していると、こ れは難しくなるでしょう。そこで必要になるのは、これらの文字をエスケ ープしているバージョンを避け、かつ、これらの文字に対するさまざまな 「不正」エンコードをうまく扱うようにしなければならないことです。 ・ クエリ:クエリのフォーマット(「?」ではじまる)がセキュリティ上のリス クになるかもしれません。というのも、クエリのフォーマットには、実際 はクライアント側で動作を起こすものがあるからです。そうあってはいけ ないし、あなたのアプリケーションも同様です。この件については Section 4.11 に詳しい情報があります。しかし重大な問題として、事実を 直視する必要があります。加えて、Web サイトの多くは、現実には「リダ イレクトを提供する場所」です。リダイレクトするには、ユーザが向かう べきところを特定できるパラメタを取得し、ユーザに新しい場所へリダイ レクトするコマンドを送り返します。攻撃者がそのようなサイトを参考に して、さらに危険な URI にリダイレクトする値を提供し、その値でブラウ ザが軽率にリダイレクトしてしまうと、これは問題になります。繰り返し になりますが、ブラウザにはもっと注意を払ってください。しかし、十分 な注意をユーザがすべて払っているわけではありません。また、Web アプ リケーションには脆弱さがある場合が多く、あるクエリ値で攻撃を受けて しまう可能性があります。しかしこれを防ぐのは困難です。公式な URI の 規格では、「+」(プラス)文字を認めていませんが、現実には「+」文字は スペース文字を表わすのによく使われています。 ・ フラグメント:フラグメントはそもそもドキュメントの一部です。文法が 正しいならば、フラグメントに対する攻撃はないと思っていましたが、そ の文法の正当さ自体をチェックする必要があります。それでも攻撃者は、 二重引用符(")のような文字を入れたり、中途半端に URI を終わらせたり できるかもしれません(チェックの裏をかいて)。 ・ URL エスケープ: URL エスケープは便利です。というのは、どんな 8 ビ ット文字も表現できるからです。しかし同時にとても危険であり、それに は訳があります。特に、URL エスケープは制御文字が表現でき、出来の良 くない Web アプリケーションの多くがこの表現に対して無防備です。実際 には URL エスケープがあろうがなかろうが、Web アプリケーションはある 文字に対して無防備です(バックスラッシュやアンパサンド等)。しかしこ れもやはり一般化するのが困難です。 ・ 相対 URI:相対 URI はかなり安全なはずですが(Web サイトをうまく運営 していれば)、アプリケーション次第で、相対 URI を許可しない方がよい ものもあります。 もちろん、単純さとのトレードオフもあります。単純なパタンは理解しやすい のですが、正確だとは言えません(単純であるがゆえにあまりに甘いか、あまり にきついかのどちらかです。それが正確なパタンであったとしても)。複雑なパ タンはより正確になり得ますが、さらにエラーが起こったり、より性能が必要 となったりする恐れがあります。また環境によっては、実行するのが困難な場 合もありえます。 ここでは私の案として、「単純かつほとんど安全な」URI パタンを紹介します 。これは「手作業」で実行できる程単純で、正規表現を使っても可能です。下 記が許可するパタンです。 (http|ftp|https)://[-A-Za-z0-9._/]+ このパタンは潜在的に危険となる可能性のあるようなクエリやフラグメント、 ポート、相対 URI 等を認めず、わずかなスキームだけしか許可していません。 これは「%」文字の使用を防ぐことで、URL エスケープを避け、サーバがうまく 扱えないかもしれない文字を特定できるようになります。また「:」や URL エ スケープも許可していないので、ポートを指定するのも認めていませし、より 危険な URI へのリダイレクトも困難になります(エスケープ文字が抜けている ため)。またその他多くの文字の利用も防ぎます。繰り返しますが、出来の良く ない Web アプリケーションは「予想外の」文字をうまく扱えません。 この「ほとんど安全な」URI でさえ、疑わしい URI をいろいろと許可してしま います。疑わしいものとは、サブディレクトリ(「/」を利用して)や上位ディレ クトリへの移動 (「..」を利用して)を試みるようなものです。この手の不正な クエリは、サーバが検知すべきです。不正なホスト ID(たとえば「20.20」)は 許可してしまいますが、これがセキュリティ上の弱点となったケースを私は知 りません。 Web アプリケーションには、サブディレクトリをクエリのデータ (もっとひどいものだと、コマンドのデータ)として扱うものもあります。これ を防ぐのは一般的に困難です。というのも、「お粗末な設計の Web アプリケー ションすべて」を見つけられる見込みは皆無だからです。パスの使用制限は可 能ですが、そうしてしまうとインターネット上の情報をほとんど参照できなく なってしまいます。またこのパタンでは、ローカルなサーバ上の情報(「http:/ //」や「http://localhost/」、「http://127.0.0.1」を使って)は参照可能で 、マシンの内部ネットワークを使ってサーバにアクセスしています。ここでは サーバが、HTTP の GET 命令の結果を何かを動かす命令ではなく、単に情報を 取得する、という正しい解釈をするという前提に立たなければなりません。 Section 4.11 でこの点を推奨しています。このパタンではクエリの書式を認め ていませんので、ほんとんどの環境ではこれで十分なはずです。 残念ながら、「ほとんど安全な」パタンが、まともで役に立つ URI も数多く防 いでしまいます。たとえば、Web サイトの多くは、「?」文字を特定のドキュメ ントを区別するのに使用しています(たとえば news サイトでの記事)。「#」文 字はドキュメント中の特定のセクションを特定するのに役に立ちますし、相対 URI を許可することで、議論が扱い易くなります。さまざまな許可された文字 や URL エスケープは「ほとんど安全な」パタンには含まれていません。たとえ ば、URL エスケープを許可しないと、英語以外のページにアクセスするのは困 難になります。本当にそのような機能が必要なら、機能が上がるほどユーザの リスクも増えるということを認識した上で、安全性が低いパタンを使ってもか まいません。 クエリは許可するが、プロトコルやポートに制限をかけるパタンは下記の通り です。私はこれを「単純でやや安全なパタン」と呼ぶことにします。 (http|ftp|https)://[-A-Za-z0-9._]+(\/([A-Za-z0-9\-\_\.\!\~\*\'\(\)\%\?]+))*/? このパタンは洗練されているわけではありません。不正なエスケープや複数の クエリ、 ftp でのクエリ等を認めているからです。ただ比較的単純という長所 は持っています。 現実には、「やや安全な」パタンの作成して、正しい値を持つ URI を制限する のは非常に難しい作業です。ここでは、現状私が試しているパタンである「手 の込んだやや安全なパタン」を載せてみます。空白は無視して、コメントは「# 」で表示してあります。 ( ( # Handle http, https, and relative URIs: ((https?://([A-Za-z0-9][A-Za-z0-9\-]*(\.[A-Za-z0-9][A-Za-z0-9\-]*)*\.?))| ([A-Za-z0-9\-\_\.\!\~\*\'\(\)]|(%[2-9A-Fa-f][0-9a-fA-F]))+)? ((/([A-Za-z0-9\-\_\.\!\~\*\'\(\)]|(%[2-9A-Fa-f][0-9a-fA-F]))+)*/?) # path (\?( # query: (([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+= ([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+ (\&([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+= ([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+)*) | (([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+ # isindex ) ))? (\#([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+)? # fragment )| # Handle ftp: (ftp://([A-Za-z0-9][A-Za-z0-9\-]*(\.[A-Za-z0-9][A-Za-z0-9\-]*)*\.?) ((/([A-Za-z0-9\-\_\.\!\~\*\'\(\)]|(%[2-9A-Fa-f][0-9a-fA-F]))+)*/?) # path (\#([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+)? # fragment ) ) 上記の手の込んだパタンでも、不正な URI すべてを禁止するわけではありませ ん。たとえば、繰り返しになりますが「20.20」は不正なドメイン名ですが、パ タンを通過してしまいます。しかし私の知るところでは、これによってセキュ リティ上の問題は発生しません。手の込んだパタンは制御文字(たとえば %00 から %FF の範囲)を表す URL エスケープを禁止しています。許可している最小 のエスケープ値は、%20(ASCII の空白) です。制御文字を禁止することで、ト ラブルはいくつか防げますが、制約もあります。すべての「2-9」を「0-9」に 変更することで、制御文字を任意の Web アプリケーションに送れるようになり ます。このパタンはパスにおいて、これ以外すべての URL エスケープを許可し ています。国際化文字には便利ですが、国際化文字を扱えないシステムでは問 題を起こします。このパタンは少なくとも URI の中で、空白や改行、二重引用 符、その他危ない文字を防ぎます。これによって、その URI を作成済みのドキ ュメントに組み込んだ時にその他の種類の攻撃を防ぎます。このパタンはあち こちで「+」を許可している点に注意してください。理由は、プラスが現実には 空白文字の代わりとして、クエリやフラグメントで使われているからです。 上記で述べたように、残念なことにクエリデータを許可すると、そのテクニッ クを使った攻撃があり、またクエリを許可してしまうと、現実に防御がほうま くできないように思えます。そこで、上記のパタンからクエリデータを扱う機 能を除いてしまうことも、やろうと思えば可能です。やり方を変えて「手の込 んだやや安全なパタン」を作成してみます。 ( ( # Handle http, https, and relative URIs: ((https?://([A-Za-z0-9][A-Za-z0-9\-]*(\.[A-Za-z0-9][A-Za-z0-9\-]*)*\.?))| ([A-Za-z0-9\-\_\.\!\~\*\'\(\)]|(%[2-9A-Fa-f][0-9a-fA-F]))+)? ((/([A-Za-z0-9\-\_\.\!\~\*\'\(\)]|(%[2-9A-Fa-f][0-9a-fA-F]))+)*/?) # path (\#([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+)? # fragment )| # Handle ftp: (ftp://([A-Za-z0-9][A-Za-z0-9\-]*(\.[A-Za-z0-9][A-Za-z0-9\-]*)*\.?) ((/([A-Za-z0-9\-\_\.\!\~\*\'\(\)]|(%[2-9A-Fa-f][0-9a-fA-F]))+)*/?) # path (\#([A-Za-z0-9\-\_\.\!\~\*\'\(\)\+]|(%[2-9A-Fa-f][0-9a-fA-F]))+)? # fragment ) ) 今言えることは、これらのパタンがユーザが選択したハイパーテキストのアン カー (「」タグ)だけをチェックしている限り、この方法で「Web のバグ」 の混入も防ぎます。 Web バグは単純なテキストで、メインページのある大元の Web サーバではない第三者が、いつ誰がそのコンテンツを読んだか、というよ うな情報を追跡できるようにします。詳しい情報は、 Section 7.7 を見てくだ さい。同じようなチェックルールで (画像)タグに使っているなら、これ は当てはまりません。画像タグは即座にロードされ、誰かが「Web バグ」を追 加できます。くどいようですが、ここではどんな属性も許可していない、とい うことを前提にしています。危険な属性はとても多く、せっかく提供しようと しているセキュリティに穴をあけてしまいます。 これらすべてのパタンは、URI がそのパタンに完全にマッチしていることが条 件になっていることをどうか忘れないでください。このパタンで不満なところ は、ある面、許容可能なパタンにも制限をかけてしまい、便利なパタンの多く を禁じてしまうところです(たとえば新たな URI スキームの利用を妨げます)。 また、Web サイトの中には 1 つのクエリを表わすのに、さらに多くのクエリが 実行されるところもあり、これを防ぐのは現実問題としてとても困難です。さ らにそのような Web サイトには、全体構成に組み込まれてしまっているものも あります。結果として、Web サイトが複数の GET クエリを 1 つの動作として 受け取る限りは、 URI は本当に安全とは言えません(Section 4.11 参照)。正 しい URL や URI についてさらに情報が知りたければ、IETF RFC 2396 を見て ください。ドメイン名の書式については、IETF RFC 1034 で詳しく論じていま す。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.10.5. その他の HTML タグ さらに HTML タグをサポートするにはどうしたら、と考えても不思議はありま せん。次に打つ手ははっきりしています。それはリスト形式のタグで、
    (ordered list)や