公開日: 2004年9月7日 | 最終更新日:
2004年9月7日
トピック
モジュールの内容
セキュリティ コード
レビューは、セキュリティ問題およびインシデントの原因となりうるセキュリティで保護されていないコーディングや脆弱性の識別に重点を置きます。コード
レビューには時間がかかりますが、開発段階でセキュリティの短所修正に要する労力およびコストは、その後の製品展開や保守サイクルで要する労力およびコストに比べてはるかに少ないため、プロジェクト開発サイクルの定期的なイベントとして行う必要があります。
このモジュールは、Microsoft® .NET Framework を使用して構築された ASP.NET Web アプリケーション
コードをレビューするうえで役立ちます。このモジュールは機能別に構成されています。また、コード レビュー
プロセスの手法およびフレームワークを把握するうえで役立つ、包括的なレビュー項目リストを含むセクションも用意されています。
目的
このモジュールの目的は次のとおりです。
| • | コード レビューと ASP.NET セキュリティ監査を行うための手法およびフレームワークを作成する。
|
| • | クロスサイト スクリプティングに対して脆弱な箇所を見つけ出す。 |
| • | SQL インジェクションに対して脆弱な箇所を見つけ出す。 |
| • | バッファ オーバーランする可能性のある箇所を見つけ出す。 |
| • | セキュリティに関する包括的な確認項目一覧の各項目について確認し、セキュリティの問題を迅速に特定する。 |
| • | 個別の .NET Framework 技術に固有のセキュリティ問題を評価する。 |
| • | 悪意のあるユーザーから攻撃を受けやすいコーディング技法を識別する。 |
| • | ソース コードおよび利用可能な .NET アセンブリを分析するため、FxCop、テキスト検索、およびILDASM
の使用法を把握する。 |
適用対象
このモジュールは、次の製品およびテクノロジに適用されます。
| • | Microsoft Windows® 2000 Server および Windows Server™ 2003 |
| • | Microsoft .NET Framework 1.1 および ASP.NET 1.1 |
| • | Microsoft SQL Server 2000 |
モジュールの使用方法
このモジュールを使用して、コード レビュー プロセスを作成、または既存のコード レビュー プロセスを拡張します。コード
レビューが開発サイクルの一部として組み込まれていることを確認し、その効果は割り当てられたリソースと予算の量に応じたものであることを認識しておく必要があります。
レビューの目標は、コードを展開する前にできるだけ多くのセキュリティの脆弱性を識別することです。開発段階でセキュリティの短所修正に要する労力およびコストは、その後の製品展開サイクルで要する労力やコストに比べてはるかに少ないからです。
このモジュールから最大限の成果を得るための参考資料は、次のとおりです。
| • | このガイドの第 3
部のセキュリティ保護に関するモジュールを使用します。このモジュールで概説するレビュー項目の詳細については、これらのモジュールを参照してください。 | • | モジュール 6「.NET セキュリティの概要」 | | • | モジュール 7「セキュリティ保護されたアセンブリを構築する」 | | • | モジュール 8「コード アクセス セキュリティの実践」 | | • | モジュール 9「ASP.NET でコード アクセス セキュリティを使用する」 | | • | モジュール 10「セキュリティ保護された ASP.NET ページとコントロールを構築する」 | | • | モジュール 11「セキュリティ保護されたサービス コンポーネントを構築する」 | | • | モジュール 12「セキュリティ保護された Web サービスを構築する」 | | • | モジュール 13「セキュリティ保護されたリモート コンポーネントを構築する」 | | • | モジュール 14「セキュリティ保護されたデータ
アクセスを構築する」 | |
| • | 次の関連チェックリストも使用します。 | • | チェックリスト: アーキテクチャと設計レビュー | | • | チェックリスト: ASP.NET をセキュリティ保護する | | • | チェックリスト: Web サービスをセキュリティ保護する | | • | チェックリスト: Enterprise Services をセキュリティ保護する | | • | チェックリスト: Remoting をセキュリティ保護する | | • | チェックリスト: データ
アクセスをセキュリティ保護する | |
FxCop
レビュー プロセスを始めるにあたって、まずコンパイル済みのアセンブリを FxCop
分析ツールにかけることをお勧めします。このツールを使用してバイナリ アセンブリ (ソース コードではありません) を分析し、それらが
MSDNで入手可能な「.NET Framework Design Guidelines」(英語)
に準拠していることを確認することができます。強固な名前がアセンブリに付けられていることも、このツールで確認されます。強固な名前には不正操作を防ぐなどのセキュリティ上の利点があります。このツールにはあらかじめルールが定義されていますが、ルールをカスタマイズまたは拡張することもできます。
詳細については、以下のリソースを参照してください。
テキスト検索の実行
レビュー
プロセスに使用するため、ファイル内の文字列を検索するテキスト検索ツールの使用法を知っていることが必要です。この種のツールを使用すると、脆弱なコードをすばやく検索することができます。このモジュールのレビュー項目では、特定の脆弱性の検索に最も適した文字列が多く示されています。
既存の検索ツールを使用していらっしゃると思いますが、Visual Studio® .NET の Find in Files、または
Microsoft Windows オペレーティング システムのコマンド ライン ツールである Findstr も使用できます。
注: エクスプローラから Windows XP 検索ツールの [ファイルに含まれる単語または句]
オプションを使用する場合、最新の Windows XP の Service Pack
がインストールされていることを確認してください。インストールされていないと、検索に失敗する可能性があります。詳細については、マイクロソフト
サポート技術情報の 309173「"ファイルに含まれる単語または句"
検索条件が機能しない」を参照してください。
ハード コード文字列の検索
ソース コードを 1 行ずつ詳細に分析する前にコード全体を高速検索し、ハード
コードされたパスワード、アカウント名、データベースの接続文字列を特定します。key、secret、password、pwd、および
connectionstring のような一般的な文字列パターンについて、コードの検索を行います。
たとえば、アプリケーションの Web
ディレクトリで password という文字列を検索する場合は、次のように、コマンド プロンプトで Findstr ツールを使用します。
findstr /S /M /I /d:c:\projects\yourweb "password" *.*
Findstr では、以下のコマンド ライン パラメータを使用します。
| • | /S — サブディレクトリも検索します。 |
| • | /M — ファイル名のみを出力します。 |
| • | /I — 大文字と小文字を区別しません。 |
| • | /D:dir — セミコロンで区切られたディレクトリの一覧を検索します。検索するファイル
パスにスペースが含まれる場合は、パスを二重引用符で囲みます。 |
Findstr の自動化
一般的な検索文字列を含むテキスト ファイルを作成することもできます。以下に示すように、Findstr を使用して作成されたテキスト
ファイルから文字列を読み込み、検索することができます。.apsx ファイルを含むディレクトリから以下のコマンドを実行します。
findstr /N /G:SearchStrings.txt *.aspx
/N は一致する各行の前に行番号を出力します。/G は検索文字列を含むファイルを示します。この例では、SearchStrings.txt
に含まれるすべての文字列の検索がASP.NET の全ページ (*.aspx) で行われます。
ILDASM
Findstr コマンドと ildasm.exe ユーティリティを組み合わせると、ハード コード文字列のバイナリ
アセンブリを検索することもできます。以下のコマンドでは、ildasm.exe を使用して ldstr
中間言語ステートメントを検索し、文字列定数を識別しています。以下の出力結果が、ハード コードされたデータベース接続、およびよく知られている SA
アカウントのパスワードを示していることに注目してください。
Ildasm.exe secureapp.dll /text | findstr ldstr
IL_000c: ldstr "RegisterUser"
IL_0027: ldstr "@userName"
IL_0046: ldstr "@passwordHash"
IL_0065: ldstr "@salt"
IL_008b: ldstr "Exception adding account. "
IL_000e: ldstr "LookupUser"
IL_0027: ldstr "@userName"
IL_007d: ldstr "SHA1"
IL_0097: ldstr "Exeception verifying password. "
IL_0009: ldstr "SHA1"
IL_003e: ldstr "Logon successful: User is authenticated"
IL_0050: ldstr "Invalid username or password"
IL_0001: ldstr "Server=AppServer;database=users; username='sa'
password=password"
注: Ildasm.exe は、\Program Files\Microsoft Visual Studio .NET
2003\SDK\v1.1\bin フォルダに保存されています。サポートされるコマンド ラインの詳細については、「ildasm.exe
/?」を実行してヘルプを参照してください。
クロスサイト スクリプティング (XSS)
入力パラメータがクライアントへ戻る出力 HTML ストリームに使用されている場合、コードのその箇所はどこもクロスサイト スクリプティング
(XSS、または CSS とも呼ばれます) 攻撃に対して脆弱です。アプリケーションが XSS に対して脆弱かどうかは、コード
レビュー前でも簡単にテストできます。ユーザー入力情報がブラウザに返されるページを検索してください。
XSS
バグは、ユーザーが入力したデータを信頼しすぎるために生じる問題の一例です。たとえば、ユーザーが価格を入力することになっているアプリケーションに、攻撃者は価格だけでなく
HTML と JavaScript を入力します。したがって、信頼関係にないソースから入力されたデータは、必ず検証することが必要です。コード
レビューでは、データが検証済みかどうかを常に確認してください。HTTP ヘッダ、クエリ文字列、フォーム データなど ASP.NET
アプリケーションのすべてのエントリ
ポイントをリストし、すべての入力がどこかのポイントで必ず検証されるようにします。このアプローチでは潜在的に危険な入力はすべて把握されていることを前提としているため、不正な入力値かどうかはテストしないてください。データが
ASP.NET アプリケーションで有効であることの確認に、正規表現を使用した方法が最も一般的に用いられます。
フォーム フィールドに XYZ などの文字列を入力して出力させるだけで簡単にテストできます。ブラウザに "XYZ" と表示される、または
HTML のソースに "XYZ" が含まれる場合、その Web アプリケーションは XSS
に対して脆弱です。より動的に確認する場合は、「<script>alert('hello');</script>」を挿入します。この手法は入力が出力生成にどのように利用されるかに依存しているため、すべての場合に有効な訳ではありません。
次の手順は、一般的な XSS に対する脆弱性を確認するうえで役立ちます。
| • | 入力を出力するコードの識別 |
| • | 潜在的に危険な HTML タグおよび属性の識別 |
| • | URL を処理するコードの識別 |
| • | 出力がエンコードされていることの確認 |
| • | 文字のエンコードが正しいかどうかの確認 |
| • | validateRequest 属性の確認 |
| • | HttpOnly Cookie オプションの確認 |
| • | <frame> セキュリティ属性の確認 |
| • | innerText および innerHTML
プロパティの使用の確認 |
入力を出力するコードの識別
ブラウザからページの出力ソースを表示させ、属性の中にコードが入っているかどうかを確認します。入っている場合、以下のコードを挿入して出力表示を再び確認します。
"onmouseover= alert('hello');"
開発者によく用いられる手法が、「<」および「>」の文字をフィルタすることです。レビューしているコードがこれらの文字をフィルタする場合、次に以下のコードを使用してテストします。
&{alert('hello');}
コードがこれらの文字をフィルタしない場合、以下のスクリプトを使用してコードをテストします。
<script>alert(document.cookie);</script>;
以下に示すように、タグを閉じてからでないと、このスクリプトが使用できない場合もあります。
"></a><script>alert(document.cookie);</script>
Write の検索
.aspx ソース コードおよびアプリケーションで開発したすべての関連アセンブリに含まれるコードで、文字列 .Write
を検索します。この検索により、Response.Write、および以下に示すような Response
オブジェクト変数を介して出力を生成する内部ルーチンを確認できます。
public void WriteOutput(Response respObj)
{
respObj.Write(Request.Form["someField"]);
}
以下に示すように、.aspx ソース コードを、同じように出力の書き込みに用いられる文字列 "<%="
で検索する必要があります。
<%=myVariable %>
表 21.1 に、Response.Write が入力フィールドとともに使用される一般的な状況を示します。
表 21.1: 考えられる入力ソース
| フォーム フィールド | Response.Write(name.Text);
Response.Write(Request.Form["name"]); |
| クエリ文字列 | Response.Write(Request.QueryString["name"]); |
| Cookie | Response.Write(
Request.Cookies["name"].Values["name"]); |
| セッションおよびアプリケーション変数 | Response.Write(Session["name"]);
Response.Write(Application["name"]); |
| データベースおよびデータ ストア | SqlDataReader reader = cmd.ExecuteReader();
Response.Write(reader.GetString(1)); |
潜在的に危険な HTML タグおよび属性の識別
以下は、完全には網羅していませんが、悪意のあるユーザーがスクリプト コードの挿入に使用する可能性がある一般的な HTML タグです。
| • | <applet> |
| • | <body> |
| • | <embed> |
| • | <frame> |
| • | <script> |
| • | <frameset> |
| • | <html> |
| • | <iframe> |
| • | <img> |
| • | <style> |
| • | <layer> |
| • | <ilayer> |
| • | <meta> |
| • | <object> |
src、lowsrc、style、href などの HTML 属性は、これらのタグと組み合わせて XSS
を引き起こす際に使用される可能性があります。
たとえば、<img> タグの src 属性は、以下の例に示すように挿入のソースとなることがあります。
<IMG SRC="javascript:alert('hello');">
<IMG SRC="java
script:alert('hello');">
<IMG SRC="java
script:alert('hello');">
<style> タグも、以下に示すように MIME タイプを変更させることにより、挿入のソースとなることがあります。
<style TYPE="text/javascript">
alert('hello');
</style>
危険なことが分かっている文字をフィルタして、コードが入力のサニタイジングを試みるかどうかを確認します。一般的に、悪意のあるユーザーは検証をバイパスできる別の表記を見つけるため、このアプローチに依存しないでください。このアプローチに依存する代わりに、セキュリティで保護された安全な既知の入力をコードが検証できるようにする必要があります。表
21.2 に、一般的な文字を表記するさまざまな方法が示されています。
表 21.2: 文字表記
| " (二重引用符) |
" | " | " | \u0022 |
| ' (一重引用符) | ' | ' | ' | \u0027 |
| & (アンパサンド) | & | & | & | \u0026 |
| < (小なり) | <
| < | < | \u003c |
| > (大なり) | > | > | > | \u003e |
URL を処理するコードの識別
URL を処理するコードが脆弱な場合があります。次のような一般的な攻撃に対して脆弱かどうかについて、コードをレビューしてください。
| • | Web サーバーが最新のセキュリティ パッチで更新されていない場合、以下のディレクトリ トラバーサルおよびダブル
スラッシュ攻撃に対して脆弱である可能性があります。 http://www.YourWebServer.com/..%255%../winnt
http://www.YourWebServer.com/..%255%..//somedirectory |
| • | コードが "/"
をフィルタする場合も、攻撃者は同じ文字に別の表記を用いることにより、簡単にフィルタを回避してしまいます。たとえば、"/" の UTF–8
表記は "%c0f%af" ですが、これを次のように URL で使用することができます。 http://www.YourWebServer.com/..%c0f%af../winnt |
| • | クエリ文字列入力を処理する場合、コードが入力データを制限して範囲チェックを行うことを確認します。攻撃者からクエリ文字列のパラメータを使った非常に大きいデータが渡された場合にも、コードが脆弱でないことを確認してください。 http://www. WebServer.com/test.aspx?var=ここに非常に大きなデータを挿入する |
出力がエンコードされていることの確認
入力のフォームおよび内容が正しいことを確認する代わりにはなりませんが、すべての入力タイプを含む HTML 出力が、HtmlEncode
を使用してエンコードされていることを確認する必要があります。URL 文字列が UrlEncode
を使用してエンコードされていないことも確認してください。入力データは、クエリ文字列、フォーム データ、Cookie、HTTP
ヘッダ、および、特にデータベースが他のアプリケーションと共有されている場合はデータベースの読み込み値である可能性があります。データをエンコードすることにより、ブラウザが
HTML を実行可能なスクリプトとして利用するのを防ぐことができます。
文字のエンコードが正しいかどうかの確認
文字エンコードが入力の表記方法を正しく制限する設定になっていることを確認して、正規化およびマルチバイトのエスケープ文字列を使用する攻撃者により、入力検証ルーチンが欺かれるのを防ぎます。
アプリケーションの Web config ファイルが、以下に示す <globalization> 要素で構成される
requestEncoding および responseEncoding 属性を持つことを確認します。
<configuration>
<system.web>
<globalization requestEncoding="ISO-8859-1" responseEncoding="ISO-8859-1" />
</system.web>
</configuration>
文字エンコードは、<meta> タグまたは以下に示すように ResponseEncoding ページ レベル属性を使用して、ページ
レベルで設定することもできます。
<% @ Page ResponseEncoding="ISO-8859-1" %>
詳細については、モジュール 10「セキュリティ保護された ASP.NET ページとコントロールを構築する」を参照してください。
validateRequest 属性の確認
.NET Framework version 1.1 を使用して構築された Web
アプリケーションでは、埋め込みスクリプトのような潜在的に悪意のある入力を取り除くため、入力のフィルタ処理を行います。この処理に依存することはできませんが、重層的な防御のために使用してください。構成ファイルの
<pages>要素で、validateRequest属性が true に設定されていることを確認します。この属性もページ
レベルで設定することができます。.aspx ソース ファイルを validateRequest で検索して、どのページも false
に設定されていないことを確認します。
HttpOnly Cookie オプションの確認
Internet Explorer 6 SP 1 は、クライアントのスクリプトが document.cookie プロパティから Cookie
へアクセスすることを阻止する、新しい HttpOnly Cookie
属性をサポートしています。読み込もうとすると、空文字列が戻されます。ユーザーが現在のドメインで Web サイトを参照する場合は、Cookie
はこれまでと同様にサーバーに送信されます。詳細については、モジュール 10「セキュリティ保護された ASP.NET
ページとコントロールを構築する」の「クロスサイト スクリプティング」を参照してください。
<frame> セキュリティ属性の確認
Internet Explorer 6 以降では、<frame> および <iframe> 要素での新しい
security 属性がサポートされています。この security 属性を使用して、ユーザーの Internet Explorer のセキュリティ
ゾーンの制限付きサイト設定を各 frame または iframe に適用することができます。詳細については、モジュール 10「セキュリティ保護された
ASP.NET ページとコントロールを構築する」の「クロスサイト スクリプティング」を参照してください。
innerText および innerHTML プロパティの使用の確認
信頼できない入力が含まれるページを作成した場合、innerHTML プロパティの代わりに innerText
プロパティを使用していることを確認します。innerText
プロパティを使用すると、コンテンツが安全に表示され、スクリプトが実行されないことが確実になります。
関連情報
XSS の詳細については、以下の資料を参照してください。
SQL インジェクション
入力パラメータを使用して SQL ステートメントを構築している箇所はすべて、SQL インジェクション攻撃に対し脆弱です。XSS
バグと同様に、SQL インジェクション攻撃は、ユーザーからの入力を信頼してその入力の内容とフォームが正しいことを検証しないことが原因です。
以下の手順は、SQL インジェクション攻撃に対する脆弱性を確認するうえで役立ちます。
| 1. | データベースにアクセスするコードを確認します。 文字列「SqlCommand」、「OleDbCommand」、または「OdbcCommand」で検索します。 |
| 2. | コードにパラメータ化ストアド プロシージャが使用されているかどうかを確認します。 ストアド
プロシージャだけでは SQL インジェクション攻撃を防ぐことはできません。コードにパラメータ化ストアド
プロシージャが使用されていることを確認してください。SqlParameter、OleDbParameter、OdbcParameter
などの型付けされたパラメータ オブジェクトがコードに使用されていることを確認します。以下に、SqlParameter
を使用した例を示します。 SqlDataAdapter myCommand = new SqlDataAdapter("spLogin", conn);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
"@userName", SqlDbType.VarChar,12);
parm.Value=txtUid.Text;
型付けされた SQL パラメータは、入力データの型および長さをチェックして、その userName
入力がデータベースで実行可能なコードではなく、リテラルな文字であることを確認します。 |
| 3. | コードに SQL ステートメントのパラメータが使用されていることを確認します。 ストアド
プロシージャを使用しない場合、コードが構築した SQL
ステートメントに、パラメータが使用されていることを確認します。次にその例を示します。 select status from Users where UserName=@userName
以下のアプローチが、入力が直接文字列連結に利用され、実行可能な SQL
ステートメントを構築している箇所で使用されていないことを確認します。 string sql = "select status from Users where UserName='"
+ txtUserName.Text + "'";
|
| 4. | コードが入力のフィルタ処理を試行するどうかを確認します。 一般的なアプローチでは、SQL
に特別な意味を持つ文字にエスケープ文字を追加するルーチンをフィルタします。これはあまり安全なアプローチとは言えず、また表記の問題があるため、この方法に依存することはできません。 |
バッファ オーバーフロー
バッファ オーバーフローのコード レビューでは、P/Invoke または COM Interop 層経由でアンマネージ
コードを呼び出すコードを特にレビューしてください。配列がアクセスするすべての箇所で配列範囲が自動的に確認されるため、マネージ コード自体のバッファ
オーバーフローに対する脆弱性はそれほど大きくありません。Win32 DLL または COM オブジェクトを呼び出したら直ちに、その API
呼び出しを慎重に検査する必要があります。
次の手順は、バッファ オーバーフローに対する脆弱性を確認するうえで役立ちます。
| 1. | アンマネージ コード呼び出し箇所を検索します。 ソース ファイルを、アンマネージ
コード呼び出しに使用する名前空間名「System.Runtime.InteropServices」で検索します。 |
| 2. | アンマネージ API に渡される文字列パラメータを確認します。 これらのパラメータはバッファ
オーバーフローのプライマリ ソースです。入力文字列の長さが API
の定めた制限を超えないことをコードが検証することを確認します。アンマネージ API が文字ポインタを受け取る場合、アンマネージ
ソースにアクセスしない限り、最大有効文字列長さを知ることができません。一般的な脆弱性を以下のコード フラグメントに示します。 void SomeFunction( char *pszInput )
{
char szBuffer[10];
// 要注意、長さ確認が行われていません。入力はバッファへ直接コピーされます。
// 長さを確認するか、strncpy を使用する必要があります。
strcpy(szBuffer, pszInput);
. . .
}
注: strncpy
はコピーされる文字数を制限するだけでコピー先に十分な領域があるかどうかの確認は行わないため、strncpy を使用してもバッファ
オーバーフローが発生する場合があります。 アンマネージ コードを所有していないため検査できない場合は、意図的に長くした入力文字列や無効な引数を使用して、API
を厳密にテストしてください。 |
| 3. | ファイル パスの長さを確認します。 アンマネージ API がファイル名やファイル
パスを受け取る場合、ファイル名やファイル パスが 260 文字を超えないことをラッパー メソッドが確認することを確認してください。これは
Win32 の MAX_PATH 定数で規定された値です。ディレクトリ名およびレジストリ キーは最大 248
文字であることにも注意してください。 |
| 4. | 出力文字列を確認します。 コードが StringBuilder を使用してアンマネージ API
から送り返された文字列を受け取ることを確認します。アンマネージ
コードからは任意の長さの文字列が送り返されるため、StringBuilder の容量がアンマネージ API
が送り返す最大文字列に足りることを確認します。 |
| 5. | 配列範囲を確認します。 配列を使用してアンマネージ API
へ入力を渡す場合、配列の容量を超えないことをマネージ ラッパーが確認することを確認します。 |
| 6. | アンマネージ コードが /GS スイッチでコンパイルされていることを確認します。 アンマネージ
コードを所有している場合、GS スイッチを使用してスタック プローブでバッファ
オーバーフローを検出してください。 |
マネージ コード
ここのレビュー項目を使用して、マネージ ソース
コード全体を分析してください。レビュー項目はアセンブリの種類に関係なく使用することができます。ここでは、一般的なマネージ
コードの脆弱性を識別するうえで役立ちます。ここで説明した問題の詳細、および脆弱性を説明したコード サンプルについては、モジュール
7「セキュリティ保護されたアセンブリを構築する」を参照してください。
マネージ コードに明示的なコード アクセス セキュリティ機能を使用している場合は、関連するレビュー ポイントについて、このモジュールの「コード
アクセス セキュリティ」を参照してください。次のレビュー項目を使用すると、マネージ コードの脆弱性を識別するうえで役立ちます。
| • | クラス デザインのセキュリティ保護 |
| • | スレッドの作成 |
| • | シリアル化の使用 |
| • | リフレクションの使用 |
| • | 例外の処理 |
| • | 暗号化の使用 |
| • | 機密情報の格納 |
| • | 委任の使用 |
クラス デザインのセキュリティ保護
アセンブリは、含まれるクラスや他の型と同程度のセキュリティでしか保護されません。次のレビュー項目は、クラス
デザインのセキュリティをレビューするうえで役立ちます。
| • | 型およびメンバの参照範囲の制限 public
に指定されている型またはメンバのすべてについてレビューを行い、アセンブリのパブリック
インターフェースの必要な一部であることを確認します。 |
| • | 非基底クラスのシールド設定 クラスを派生させたくない場合は、キーワード "sealed"
を使用して、コードが潜在的に悪意のあるサブクラスに誤用されることを防ぎます。 パブリックな基底クラスについては、コード アクセス
セキュリティの継承要求を使用して、クラスから継承できるコードに制限することができます。これは、重層的な防御に有効です。 |
| • | フィールドの公開に対するプロパティの使用 クラスが直接フィールドを公開していないことを確認してください。プロパティは、プライベート以外のフィールドの公開に使用してください。このことにより、入力値を検証し、セキュリティ
チェックを追加することになります。 |
| • | 読み取り専用プロパティの使用 読み取り専用プロパティを効果的に使用していることを確認します。フィールドが読み取り専用を設定するデザインになっていない場合は、get
アクセサだけを追加して、読み取り専用プロパティを実装します。 |
| • | 仮想内部メソッドの使用 これらのメソッドはクラスにアクセスした他のアセンブリにオーバーライドされることがあります。宣言型チェックを使用するか、または必要条件でない場合はキーワードの
virtual を削除してください。 |
| • | IDisposable の実装 実装している場合は、オブジェクト インスタンスが終了したときに Dispose
メソッドを呼び出して、すべてのリソースが開放されていることを確認します。 |
スレッド の作成
マルチスレッド
コードは、見つけにくいタイミング関連のバグまたはセキュリティの脆弱性の原因となりうる競合を引き起こしやすいコードです。マルチスレッド
コードの場所を確認するには、以下のコード フラグメントに示すようにソース コードを「Tread」で検索して、新しい Tread
オブジェクトが作成された場所を識別してください。
Thread t = new Thread(new ThreadStart(someObject.SomeThreadStartMethod));
次のレビュー項目を使用すると、スレッドの脆弱性を識別するうえで役立ちます。
| • | コードによるセキュリティ チェックの結果のキャッシュ 静的変数やグローバル変数などの形でコードのセキュリティ
チェック結果がキャッシュされ、そのフラグを使用して次のセキュリティに関する決定が行われる場合、そのコードは競合に対して特に脆弱になります。 |
| • | コードの偽装 新しいスレッドを作成するスレッドは現在偽装していますか。新しいスレッドは、既存のスレッドのセキュリティ
コンテキストではなく、プロセス レベルのセキュリティ コンテキストを取得します。 |
| • | コードにおける静的クラス
コンストラクタ 複数のスレッドが同時にアクセスする場合にも、静的クラスのコンストラクタが脆弱でないことを確認します。必要に応じて、スレッドを同期させ、同時アクセスを防ぎます。 |
| • | Dispose メソッドの同期 オブジェクトの Dispose メソッドが同期されていない場合、2
つのスレッドが同じオブジェクトに対して Dispose を実行する可能性があります。この同時実行は、特に cleanup
コードがファイル、プロセス、スレッド処理などのアンマネージ リソース
ハンドラを開放した場合、セキュリティ問題を発生させる可能性があります。 |
シリアル化の使用
シリアル化をサポートするクラスは SerializableAttribute に指定されるか、あるいは Iserializable
から派生します。シリアル化をサポートするクラスは、文字列 Serializable
のテキスト検索によって見つけることができます。次に、以下の問題についてコードをレビューします。
| • | クラスでの機密性の高いデータの含有 機密性の高いデータが含まれる場合、[NonSerialized]
属性に指定、あるいは ISerializable
を実装してどのフィールドがシリアル化されるかを制御することによって、コードが機密性の高いデータのシリアル化を防いでいることを確認します。 クラスが機密性の高いデータをシリアル化する必要がある場合、データの保護方法についてレビューします。まずデータの暗号化を検討します。 |
| • | クラスへの Iserializable の実装 実装している場合、クラスは
AllowPartiallyTrustedCallersAttribute
を含まない強固な名前のアセンブリにインストールされた呼び出し元など、完全に信頼された呼び出し元だけをサポートしていますか?
クラスが部分的に信頼された呼び出し元をサポートしている場合、GetObjectData
メソッド実装節が適切なアクセス許可を要求を使用して呼び出しコードを承認することを確認してください。"StrongNameIdentityPermission"
要求を使用してオブジェクトをシリアル化できるアセンブリを制限する手法が有効です。 |
| • | クラスによるデータ ストリームの検証 コードにシリアル化されたデータ
ストリームを受け取るメソッドが含まれる場合、すべてのフィールドがデータ
ストリームから読み出されたときに検証されることを確認します。 |
リフレクションの使用
リフレクションを使用するコードを確認するには、リフレクション型を含む名前空間の "System.Reflection"
で検索を行います。リフレクションを使用する場合、次のレビュー項目を確認すると、潜在的な脆弱性を識別するうえで役立ちます。
| • | アセンブリの動的な読み込み コードがアセンブリを読み込んでオブジェクト
インスタンスの作成または型の呼び出しを行う場合、そこに入力データから得られたアセンブリまたは型の名前が含まれますか。含まれる場合、コードがアクセス許可の要求で保護され、すべての呼び出しコードが承認されていることを確認します。たとえば、StrongNameIdentity
によるアクセス許可の要求または完全信頼の要求を使用してください。 |
| • | ランタイムでの動的なコードの作成 アセンブリがコードを動的に生成して呼び出し元に対する処理を実行する場合、生成されるコードは呼び出し元から何の影響も受けないことを確認します。たとえば、コードの生成は呼び出し元からの入力パラメータに依存していますか。入力パラメータに依存することは避けてください。どうしても必要な場合は、入力が検証されており、悪用してコードの生成に影響を与えることができないことを確認してください。 |
| • | 他の型に対するリフレクションの使用 使用する場合は、信頼されたコードからしか呼び出されないことを確認してください。コード
アクセス セキュリティ許可を要求して、呼び出しコードを承認してください。 |
例外の処理
セキュリティで保護された例外処理は、堅牢なコードに必要です。例外の詳細なログを確実に記録して問題の診断に役立て、内部システムの詳細がクライアントに公開されるのを確実に防ぎます。次のレビュー項目を確認すると、例外処理の脆弱性を識別するうえで役立ちます。
| • | 初期段階での障害の発生 リソースを消費する不要な処理を回避するため、コードが早い段階で障害を発生させることを確認します。コードに障害が発生した場合、そのエラーによってユーザーがセキュリティ
チェックを回避して権限によるコードを実行できるようにならないことを確認します。 |
| • | 例外の処理方法 呼び出し元にシステムまたはアプリケーションの詳細を公開することは避けてください。たとえば、呼び出しスタックをエンド
ユーザーに返さないでください。try/catch ブロックに関する例外を生成する可能性のあるリソース
アクセスまたは操作はラップします。処理方法を知っている例外のみ処理し、特定の例外を一般的なラッパーで処理することは避けてください。 |
| • | 例外の詳細のログ記録 問題の診断に役立てるため、例外の詳細ログが例外のソースに記録されていることを確認します。 |
| • | 例外フィルタの使用 使用する場合は、呼び出しスタックの上の方にあるフィルタのコードは、finally
ブロックのコードより前に実行されることに注意してください。finally
ブロックの状態の変更は例外フィルタの実行の後に行われるため、finally
ブロックでの状態の変更に依存していないことを確認します。 例外フィルタの脆弱性の例については、モジュール
7「セキュリティ保護されたアセンブリを構築する」の「例外管理」を参照してください。 |
暗号化の使用
使用する場合は、コードが自身の暗号化ルーチンを実装していないことを確認します。暗号化ルーチンを実装する代わりに、名前空間
System.Security.Cryptography、あるいはデータ保護 API (DPAPI) などの Win32
暗号化を使用する必要があります。次のレビュー項目を確認すると、潜在的な暗号化関連の脆弱性を識別するうえで役立ちます。
| • | 対称暗号化の使用 対称暗号化を使用し、暗号化されたデータを長期間変動させずにおく必要がある場合には、Rijndael
(AES (Advanced Encryption Standard) または 3DES (Triple Data Encryption
Standard) と呼びます) を使用することを確認します。脆弱な (ただし速い) RC2 や DES アルゴリズムは、セッション
データなどデータを短期間だけ暗号化する場合に使用してください。 |
| • | 最大サイズのキーの使用 使用しているアルゴリズムで最も大きなサイズのキーを使用してください。キーのサイズが大きいとキーに対する攻撃はより困難になりますが、パフォーマンスが低下します。 |
| • | ハッシュの使用 使用する場合で、共有の機密情報を知っていることを証明するプリンシパルが必要な場合は、MD5 や
SHA1
を使用することを確認してください。たとえば、チャレンジ/レスポンス認証では、パスワードをサーバーに渡すことなくクライアントがパスワードを知っていることを証明するために、ハッシュが使用されます。クライアントとのキーの共有が必要となるメッセージ認証コード
(MAC) と共に HMACSHA1 を使用してください。このことにより、セキュリティ
チェックを統合し、ある程度十分な認証を行うことができます。 |
| • | 暗号化のための乱数の生成 暗号化のための乱数を生成する場合は、コードが Random クラスではなく
System.Security.Cryptography.RNGCryptoServiceProvider
クラスを使用して乱数を生成することを確認します。Random
クラスでは、繰り返し不可能または予測不可能な真の乱数は生成されません。 |
機密情報の格納
アセンブリが機密情報を格納する場合、デザインをレビューして機密情報の格納が絶対必要であることを確認します。機密情報を格納する必要がある場合、次のレビュー項目を確認してできるだけセキュリティで保護してください。
| • | 機密情報のメモリへの保存 機密情報をプレーンテキストの状態でメモリに長期間格納しないでください。ストアから取得した機密情報を復号化して使用したら、機密情報が格納されていた領域をゼロに置き換えてください。 |
| • | プレーンテキストのパスワードまたは SQL 接続文字列の、Web.config または Machine.config
への保存 これは行わないでください。aspnet_setreg.exe を使用して、暗号化された資格情報を
<identity>、<identity>、および <identity>
要素のレジストリに格納してください。Aspnet_setreg.exe の取得および使用方法の詳細については、マイクロソフト
サポート技術情報の 329290「[HOWTO]
ASP.NET
ユーティリティを使用して資格情報およびセッション状態の接続文字列を暗号化する方法」を参照してください。 |
| • | 機密情報を暗号化する方法 接続文字列や資格情報が DPAPI
を使用して暗号化されていることを確認します。LSA へのアクセスに使用されるアカウントは管理権限を必要とするため、ローカル
セキュリティ機関 (LSA) に機密情報を格納しないでください。DPAPIの使用方法の詳細については、「セキュリティ保護された
ASP.NET アプリケーションの構築」の「パート IV :
参照」の「作業の手引き」セクションの「DPAPIライブラリを作成する方法」を参照してください。 |
| • | 機密情報のレジストリへの保存 格納する場合は、その情報が最初に暗号化されること、および格納先が
HKEY_LOCAL_MACHINE の場合は制限付き ACL で保護されることを確認してください。コードが
HKEY_CURRENT_USER を使用している場合、関連ユーザー アカウントでの実行が自動的に制限されるため、ACL
での保護は必要ありません。 |
| • | リバース
エンジニアリングの問題 心配な場合は、難読化ツールの使用を検討してください。詳細については、難読化ツールのリスト「OBFUSCATORS」(英語)
を参照してください。 注:
機密データの隠蔽を暗号化ツールに依存しないでください。暗号化ツールを使用すると機密データの識別がより困難になりますが、問題は解決されません。 |
委任の使用
どのコードも、メソッドと委任を関連付けることができます。委任には、低い信頼レベルで実行される潜在的に悪意のあるコードも含まれます。
| • | 信頼関係にないソースからの委任の受け取り 受け取る場合は、SecurityAction.PermitOnly
によるセキュリティ許可を使用して、委任 メソッドへのアクセス許可が制限されていることを確認してください。 |
| • | 委任の呼び出し前におけるアサートの使用 それが何を行う委任
コードか呼び出し前には分からないため、これは行わないようにしてください。 |
コード アクセス セキュリティ
すべてのマネージ コードに対し、コード アクセス
セキュリティ許可が要求されます。問題の多くは、コードが部分的に信頼された環境で使用され、コードまたは呼び出しコードがコード アクセス セキュリティ
ポリシーから十分に信頼されない場合にのみ、発生します。
ここで説明した問題の詳細については、モジュール 8「コード アクセス セキュリティの実践」を参照してください。
以下のレビュー ポイントを使用して、適切で安全なコード アクセス セキュリティが使用されていることを確認してください。
| • | 部分的に信頼された呼び出し元のサポート |
| • | パブリックな型およびメンバへのアクセスの制限 |
| • | 宣言型セキュリティの使用 |
| • | Assert の呼び出し |
| • | 必要に応じた、アクセス許可の要求の使用 |
| • | リンク確認要求の使用 |
| • | Deny または PermitOnly の使用 |
| • | 特に危険なアクセス許可の使用 |
| • | /unsafe オプションを指定したコンパイル |
部分的に信頼された呼び出し元のサポート
コードが部分的に信頼された呼び出し元をサポートする場合、攻撃される危険性が大きくなります。その結果、広範囲に渡る徹底したコード
レビューを行うことが特に重要になります。Web アプリケーションの <trust>
レベルが、部分的に信頼されたレベルで実行される設定になっているかどうかを確認してください。この設定になっている場合、そのアプリケーション用に開発されるアセンブリは、部分的に信頼された呼び出し元をサポートする必要があります。
次のレビュー項目を確認すると、潜在的に脆弱な領域を識別するうえで役立ちます。
| • | アセンブリに対する強固な名前付け 強固な名前付けが行われている場合、既定のセキュリティ
ポリシーでは、部分的に信頼された呼び出し元からは呼び出せない設定になっています。完全な信頼へのリンク確認要求が、共通言語ランタイム
(CLR)
により暗黙的に出されます。アセンブリに強固な名前が付けられていない場合、明示的に完全な信頼を要求するなど呼び出し元を制限する明示的な方法を取らない限り、そのアセンブリはどのコードからも呼び出すことができます。 注: ASP.NET アプリケーションに呼び出される強固な名前のアセンブリは、グローバル アセンブリ
キャッシュにインストールする必要があります。 |
| • | APTCA の使用 強固な名前のアセンブリに
AllowPartiallyTrustedCallersAttribute
が含まれる場合、部分的に信頼された呼び出し元はコードを呼び出すことができます。この場合、アセンブリによって実行されるリソースへのアクセスなどの権限による操作は、他のコード
アクセス セキュリティ要求で承認および保護されていることを確認してください。.NET Framework クラス
ライブラリを使用してリソースにアクセスする場合、自動的に完全なスタック ウォーク要求が出されます。コードが Assert
呼び出しを使用してスタック ウォークを妨げない限り、呼び出しコードは承認されます。 |
| • | オブジェクト参照の引き渡し メソッドの戻り値および ref
パラメータを確認して、コードがオブジェクト参照を返す場所を確認してください。部分的に信頼されたコードが、完全に信頼された呼び出し元へのアクセスを要求するアセンブリから取得されたオブジェクトに参照を渡さないことを確認します。 |
パブリックな型およびメンバへのアクセスの制限
コード アクセス セキュリティ ID
要求を使用して、パブリックな型およびメンバへのアクセスを制限することができます。これは、アセンブリが攻撃を受ける危険性を減らすのに役立ちます。
| • | 呼び出し元の制限における ID 要求の使用
特定のアプリケーション内で特定のアセンブリによってのみ使用されるクラスまたは構造については、ID
要求を使用して呼び出し元の範囲を制限することができます。たとえば、StrongNameIdentityPermission
を使用して要求すると、要求された公開キーに対応する署名付き秘密キーを持つ特定のアセンブリに呼び出し元を制限することができます。 |
| • | サブクラスの制限に対する継承要求の使用 特定のコードのみが基底クラスからの継承を必要とすることが分かっている場合、クラスが
StrongNameIdentityPermission
を使用して継承要求することを確認します。 |
宣言型セキュリティ属性の使用
宣言型セキュリティ属性は Permview.exe.
などのツールを使用して表示させることができます。これは、アセンブリの利用者および管理者がコードのセキュリティ要件を理解するうえで非常に役立ちます。
| • | 最小限のアクセス許可の要求 .RequestMinimum
文字列を検索して、コードがアクセス許可の要求を使用して最小アクセス許可要件を特定しているかどうかを確認してください。これは、アセンブリのアクセス許可の要求を明確に記録するために必ず行ってください。 |
| • | 必要に応じたアクセス許可の要求、またはアクセス許可の拒否 文字列 .RequestOptional と
.RequestOptional を検索してください。この2
つのアクションのいずれかを使用して最小権限のコードを開発した場合、そのコードは
AllowPartiallyTrustedCallersAttribute
で指定されない限り強固な名前のアセンブリを呼び出せなくなることに注意してください。 |
| • | 宣言型セキュリティではなく強制型セキュリティの使用 ロジックを適用してどの許可を要求すべきかの決定しなくてはならない、または要求にランタイム変数が必要なために、コードに強制型チェックが必要となる場合があります。特定のロジックが必要でないなら、アセンブリのアクセス許可の要求を記録するために、宣言型セキュリティの使用を検討してください。 |
| • | クラスとメンバのレベル属性の組み合わせ これは行わないでください。メソッドやプロパティなどのメンバ属性はクラス
レベル属性を同じセキュリティ アクションと置き換えるため、これらと結合しないでください。 |
Assert の呼び出し
Assert 呼び出しについてコードを検索してください。この検索により Debug.Assert
のインスタンスが見つかることがあります。コードがどこで CodeAccessPermission オブジェクトに Assert
を呼び出すかを探します。コード アクセス許可をアサートする場合は、危険性のあるプラクティスであるスタック ウォークを要求するコード アクセス
セキュリティ許可を回避してください。悪意のある呼び出し元が、セキュリティで保護されたリソースまたは権限による操作にアクセスするアサートを行えないようにするための、コードにおける処理方法を確認してください。次のレビュー項目を確認してください。
| • | Demand と Assert のパターンの使用 コードが Demand を Assert
より前に発行することを確認してください。コードは、アンマネージ
コードの許可などの広範な許可がアサートされる前に、呼び出し元を承認する細かい許可を要求する必要があります。 |
| • | Assert 呼び出しと RevertAssert 呼び出しとのマッチング 各 Assert 呼び出しと
RevertAssert 呼び出しが対応していること確認してください。Assert を呼び出すメソッドが戻されると Assert
は暗黙的に削除されますが、優れたプラクティスでは Assert 呼び出し後できるだけ迅速に RevertAssert
を明示的に呼び出します。 |
| • | アサート時間の短縮 必要最小限の時間だけアサート許可が与えられていることを確認してください。たとえば、別のメソッドを呼び出す間だけ
Assert 呼び出しを使用する必要がある場合、そのメソッド呼び出し後直ちに RevertAssert
が呼び出されていることを確認してください。 |
必要に応じた、アクセス許可の要求の使用
コードは常に .NET Framework クラス
ライブラリからのアクセス許可の要求チェックを受けますが、コードが明示的なアクセス許可の要求を使用する場合、適切に要求されていることを確認してください。コードを文字列.
Demand で検索して、宣言型または強制型のアクセス許可の要求のどちらであるかを識別し、次に次のレビュー項目を確認します。
| • | データのキャッシュ キャッシュする場合、キャッシュされたデータにアクセスする前にコードが適切なアクセス許可の要求を発行しているかどうかを確認してください。たとえば、データがあるファイルから取得され、その呼び出しコードがキャッシュを割り当てたファイルへアクセスできる承認を受けたことを確認したい場合、キャッシュされたデータにアクセスする前に
FileIOPermission を要求します。 |
| • | カスタム リソースまたは権限による操作の公開 コードがアンマネージ コードを通じてカスタム
リソースまたは権限による操作を公開する場合、適切なアクセス許可の要求が発行されることを確認します。この要求はリソースの性質によって、ビルトイン許可型のこともあればカスタム許可型のこともあります。 |
| • | 初期段階でのアクセス許可の要求 アクセス許可の要求が、リソースへのアクセスまたは権限による操作を行う前に発行されていることを確認してください。リソースにアクセスし、その次に呼び出し元の承認を行わないでください。 |
| • | 冗長アクセス要求の発行 .NET Framework のクラス
ライブラリを使用するコードは、アクセス許可を要求されます。コードは同じアクセス要求を発行する必要がありません。同じアクセス要求を発行すると、スタック
ウォークの重複または無駄になります。 |
リンク確認要求の使用
一般的な確認要求とは異なり、リンク確認要求はコードの直接の呼び出し元だけをチェックします。完全なスタック
ウォークは実行されないため、リンク確認要求を使用するコードはおとり攻撃にさらされます。おとり攻撃の詳細については、モジュール 8「コード アクセス
セキュリティの実践」を参照してください。
コードを文字列 .LinkDemand
で検索し、リンク確認要求が使用されている箇所を識別してください。これらは宣言的にのみ使用されます。一例を以下のコード
フラグメントに示します。
[StrongNameIdentityPermission(SecurityAction.LinkDemand,
PublicKey="00240000048...97e85d098615")]
public static void SomeOperation() {}
ここで説明した問題の詳細については、モジュール 8「コード アクセス
セキュリティの実践」の「リンク要求」を参照してください。次のレビュー項目は、コードのリンク確認要求の使用法についてレビューするうえで役立ちます。
| • | リンク確認要求使用の理由 防御を考慮したアプローチでは、リンク確認要求をできるだけ避けます。単にパフォーマンスを向上させるためや完全なスタック
ウォークを取り除くためだけに使用しないでください。ネットワークの遅延やデータベース アクセスなど他の Web
アプリケーションのパフォーマンス問題のコストに比べ、スタック
ウォークのコストは小さいです。リンク確認要求は、どのコードによりコードが呼び出されるかを把握していて、それを制限できる場合のみ安全です。 |
| • | 呼び出し元の信頼性 リンク確認要求を使用する場合、おとり攻撃の防止をを呼び出し元に依存しています。リンク確認要求は、コードの直接の呼び出し元を正確に知っていてそれを制限でき、またその先の呼び出し元を呼び出し元が承認すると信頼できる場合にのみ、安全です。 |
<