Web アプリケーション セキュリティの強化


Web アプリケーション セキュリティ強化: 脅威とその対策

セキュリティ保護された ASP.NET ページとコントロールを構築する

公開日: 2004年9月7日 | 最終更新日: 2004年9月7日
トピック

 モジュールの内容
 目的
 適用対象
 モジュールの使用方法
 脅威とその対策
 設計に関する考慮事項
 入力検証
 クロスサイト スクリプティング
 認証
 承認
 偽装
 機密性の高いデータ
 セッション管理
 パラメータ改ざん
 例外管理
 監査とログ記録
 要約
 その他のリソース

モジュールの内容

Web ページとコントロールは、アプリケーションの防御の最前線であり、アプリケーションのセキュリティの侵害に没頭している攻撃者により集中的に調査されることがあります。多くの場合、こうした攻撃では、最終的にバックエンド システムとデータ ストアが狙われます。

コード インジェクション、クロスサイト スクリプティング (XSS) などのアプリケーション攻撃では、壊滅的な結果を招いたり、情報の漏えい、なりすまし、特権の昇格、リモート コードの実行につながるおそれのある、サーバー側アプリケーションの脆弱性が利用されます。セキュリティ保護された Web ページとコントロールを構築するには、このモジュールで説明する正しいプログラミング方法に従う必要があります。

このモジュールでは、ASP.NET ページとコントロールの一般的な脅威および関連する対策を挙げて説明することから始めます。この後に、対処する必要があるアプリケーション セキュリティ領域の包括的な一覧が続きます。このような領域には、入力検証、出力エンコード、認証、承認、偽装、機密性の高いデータの保護、安全なセッション管理、パラメータ改ざんの保護、例外管理などがあります。こうしたテクノロジは、多層防御セキュリティ ソリューションの基本的な部分です。見過ごされがちですが、前述の脅威により、インフラストラクチャの安全性にかかわらず、攻撃者は間違いなくシステムのセキュリティを侵害できます。

目的

このモジュールの目的は次のとおりです。

  • セキュリティ保護された ASP.NET ページとコントロールを設計する。

  • 正規表現や他のテクノロジを使用してセキュリティ保護された検証コードを開発する。

  • クロスサイト スクリプティング (XSS) を防御する。

  • ユーザーを認証して承認する。

  • セキュリティ保護されたフォーム認証を開発する。

  • 例外の詳細がクライアントに届かないようにする。

  • ASP.NET セッションの管理と保護を行う。

  • パラメータ改ざんを防止する。

  • コード インジェクション、セッション ハイジャック、なりすまし、パラメータ改ざん、ネットワーク盗聴、情報の漏えい、クロスサイト スクリプティング (XSS)、Cookie リプレイ攻撃などの一般的な脅威に対処するためにどの対策を適用するかを見分けます。

適用対象

このモジュールは、次の製品およびテクノロジに適用されます。

  • Microsoft® Windows® 2000 Server および Microsoft Windows Server™ 2003

  • Microsoft .NET Framework 1.1 および ASP.NET 1.1

モジュールの使用方法

提示された安全なプログラミング方法のほかに、このガイドの対応するモジュールを活用すれば、セキュリティ保護された ASP.NET ページとコントロールの作成に役立ちます。

  • モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」の手順を実行します。このモジュールは、Machine.config と Web.config のセキュリティ保護された設定で ASP.NET を適切に構成するのに役立ちます。

  • このガイドのチェックリスト セクションに記載されているチェックリストを使用します。「チェックリスト: ASP.NET をセキュリティ保護する」では、このモジュールの推奨事項とモジュール 19 の推奨事項が結合されています。このガイダンスを適用していることを確認してください。

  • ASP.NET ページとコントロールに特有な脅威と攻撃を理解します。このモジュールのガイドラインに従って対策を適用します。

  • モジュール 4 「セキュリティ保護された Web アプリケーションの設計ガイドライン」を参照してください。このモジュール (モジュール10) の推奨事項の多くは、モジュール 4 で説明した設計ガイドラインに基づいています。

  • 設計者は、このモジュールの「設計に関する考慮事項」セクションを利用する必要があります。

  • 開発者は、このモジュールのガイダンスを開発プロセスに適用する必要があります。最上位のアプリケーション レベルの攻撃の大多数がこの領域の脆弱性を利用するため、開発者は、入力データ検証に特に注意を払う必要があります。

  • ASP.NET ページとコントロール セキュリティを微調整するために、コントロールをプログラムの観点から学習します。

  • 一般的な問題に取り組む手段としてアプリケーション脆弱性カテゴリを使用します。アプリケーション脆弱性カテゴリには、問題の取り組みやグループ化に役立つ方法が用意されています。

脅威とその対策

ほとんどの Web アプリケーション攻撃では、HTTP 要求の中で悪質な入力を渡す必要があります。攻撃の一般的な目的は、許可されていない操作をアプリケーションで強制的に実行させることや、アプリケーションの通常の操作を中断させることです。このため、徹底的な入力検証は、多くの攻撃に対して不可欠な対策であり、ASP.NET Web ページとコントロールを開発するときの最優先事項でなければなりません。主要な脅威には、以下のようなものがあります。

  • コード インジェクション

  • セッション ハイジャック

  • なりすまし

  • パラメータ改ざん

  • ネットワーク盗聴

  • 情報の漏えい

図 10.1 は、Web アプリケーションに対する最も一般的な脅威を示しています。

ASP.NET Web ページとコントロールに対する一般的な脅威

図 10.1
ASP.NET Web ページとコントロールに対する一般的な脅威

コード インジェクション

コード インジェクションは、攻撃者がアプリケーションのセキュリティ コンテキストを使用して任意のコードを実行できる場合に行われます。アプリケーションが特権アカウントを使用して実行する場合に、このリスクは高まります。

攻撃

コード インジェクション攻撃は各種あります。たとえば、以下が挙げられます。

  • クロスサイト スクリプティング: 悪質なスクリプトが、入力として Web アプリケーションに送信されます。このスクリプトは、ユーザーのブラウザにエコー バックされ、そこで実行されます。

  • バッファ オーバーフロー: マネージ コードのタイプ セーフ検証によりリスクが大幅に低下しますが、それでもアプリケーションは、特にアンマネージ コードを呼び出す場合に攻撃を受けるおそれがあります。バッファ オーバーフローでは、攻撃者が Web アプリケーション プロセス内部で、そのセキュリティ コンテキストを利用して任意のコードを実行できます。

  • SQL インジェクション: この攻撃は、脆弱なデータ アクセス コードを標的にします。攻撃者は、対象とするクエリを変更する SQL 入力、またはデータベースにおいてまったく新しいクエリを実行する SQL 入力を送信します。一般的にフォーム認証ログオン ページが標的になるのは、ユーザー ストアを照会するためにユーザー名とパスワードが使用されるためです。

脆弱性

コード インジェクション攻撃の成功につながる脆弱性には、以下のようなものがあります。

  • 入力検証またはクライアント側入力検証に関する信頼性が不十分または欠落している。

  • HTML 出力に検証されていない入力が含まれる。

  • 入力されたパラメータを使用しない SQL ステートメントを動的に作成する。

  • 過剰な特権を持つプロセス アカウントとデータベース ログオンを利用する。

対策

以下の対策を行えば、コード インジェクションを防止できます。

  • 攻撃者がスクリプト コードを注入したり、バッファ オーバーフローを発生させたりできないように、入力を検証します。

  • 入力を含むすべての出力をエンコードします。このようにすれば、悪質なスクリプト タグが挿入されていても、クライアントのブラウザでコードとして解釈されるのを防止できます。

  • 悪質な SQL 入力がデータベースで実行可能なステートメントとして処理されないように、パラメータを受け入れるストアド プロシージャを使用します。

  • 最低限の特権プロセスと偽装アカウントを使用します。このようにすれば、リスクを軽減し、攻撃者がアプリケーションのセキュリティ コンテキストを利用してコードを実行するようにした場合でも、被害を少なくすることができます。

セッション ハイジャック

"セッション ハイジャック" は、攻撃者が認証トークンを獲得して別のユーザーのセッションを制御する場合に発生します。認証トークンは、一般に Cookie または URL に格納されます。攻撃者が認証トークンを獲得した場合は、それを要求と共にアプリケーションに送信できます。アプリケーションでは、この要求を正当なユーザーのセッションに関連付けます。そのため、このセッションを利用すれば、攻撃者は、認証済みアクセスを必要とする、アプリケーションの制限区域にアクセスできます。その結果、攻撃者は正当なユーザーの識別情報と特権を受け継ぎます。

脆弱性

Web ページとコントロールがセッション ハイジャックの影響を受けやすくなる一般的な脆弱性には、以下のようなものがあります。

  • URL 内の保護されていないセッション識別子

  • 個人情報 Cookie と認証 Cookie の混在

  • 暗号化されていないリンク経由で渡される認証 Cookie

攻撃

セッション ハイジャック攻撃には、以下のようなものがあります。

  • Cookie リプレイ: 攻撃者は、認証 Cookie を捕捉するために、ネットワーク監視ソフトウェアを使用するか、他の何らかの手段を利用します。たとえば、XSS スクリプティング脆弱性を悪用します。

  • クエリ文字列操作: 悪質なユーザーが、URL クエリ文字列で明確に表示されているセッション識別子を変更します。

対策

以下の対策を採用すれば、セッション ハイジャックを防止できます。

  • 個人情報 Cookie と認証 Cookie を分離します。

  • HTTPS 接続経由の場合にのみ認証 Cookie を送信します。

  • クエリ文字列では認証済みユーザーを表すセッション識別子を引き渡しません。

  • 発注、送金などの重要な操作を実行する前にユーザーを再認証します。

なりすまし

"なりすまし" は、悪質なユーザーが、アプリケーションにアクセスできるように、正当なユーザーのふりをする場合に発生します。

脆弱性

Web ページとコントロールがなりすまし攻撃の影響を受けやすくなる一般的な脆弱性には、以下のようなものがあります。

  • 暗号化されていないリンク経由で渡される認証資格情報

  • 暗号化されていないリンク経由で渡される認証 Cookie

  • 脆弱なパスワードとポリシー

  • ユーザー ストア内の脆弱な資格情報ストレージ

攻撃

なりすまし攻撃には、以下のようなものがあります。

  • Cookie リプレイ: 攻撃者は、認証 Cookie を捕捉するために、ネットワーク監視ソフトウェアを使用するか、XSS 攻撃を利用します。その結果、攻撃者は Cookie をアプリケーションに送信し、偽装アクセスします。

  • ブルート フォース パスワード攻撃: 攻撃者が、ユーザー名とパスワードの組み合わせを何度も試します。

  • 辞書攻撃: この自動化したブルート フォース パスワード攻撃では、辞書中のあらゆる語をパスワードとして試します。

対策

以下の対策を採用すれば、なりすましを防止できます。

  • HTTPS 接続経由の場合にのみ認証資格情報と認証 Cookie を送信します。

  • 強固なパスワードを指定します。正規表現を利用すれば、ユーザー指定のパスワードを適度に複雑にするという要件を確実に満たすことができます。

  • パスワード検証をデータベースに格納します。辞書攻撃のリスクを軽減するためにランダムな salt 値と組み合わせた非可逆パスワード ハッシュを格納します。

パスワード ハッシュと他のシークレットをデータベースに格納する方法の詳細については、モジュール 14 「セキュリティ保護されたデータ アクセスを構築する」を参照してください。

パラメータ改ざん

パラメータとは、ネットワーク経由でクライアントからサーバーに渡されるデータ項目のことです。パラメータには、フォーム フィールド、クエリ文字列、表示状態、Cookie、HTTP ヘッダーなどがあります。機密性の高いデータ、またはサーバーでのセキュリティ意思決定に使用されるデータが、保護されていないパラメータを使用して渡される場合、アプリケーションは、情報の漏えいまたは許可されていないアクセスに対して潜在的に脆弱です。

脆弱性

パラメータ改ざんにつながる脆弱性には、以下のものがあります。

  • 機密性の高いデータを含む隠しフォーム フィールドまたはクエリ文字列の使用

  • 暗号化されていない接続でのセキュリティに関係するデータを含む Cookie の転送

攻撃

パラメータ改ざん攻撃には、以下のようなものがあります。

  • Cookie リプレイ攻撃: 攻撃者は、Cookie を捕捉して変更し、それをアプリケーションに対して再利用します。このため、Cookie にサーバーでの認証または承認に使用されるデータが含まれている場合は、簡単になりすましや特権の昇格につながるおそれがあります。

  • 隠しフォーム フィールドの操作: こうしたフィールドには、サーバーでのセキュリティ意思決定に使用されるデータが含まれています。

  • クエリ文字列パラメータの改ざん:

対策

以下の対策を採用すれば、パラメータ改ざんを防止できます。

  • クライアント側の状態管理オプションを利用しません。表示状態、Cookie、クエリ文字列、または隠しフォーム フィールドのようなクライアント側の状態管理オプションのいずれかを使用して機密性の高いデータを格納するのは避けます。

  • 機密性の高いデータはサーバーに格納します。サーバーで保持されている機密性の高いデータ項目にユーザーのセッションを関連付けるには、セッション トークンを使用します。

  • セッション トークンを保護するには、メッセージ認証コード (MAC) を使用します。このコードをサーバーの認証、承認、およびビジネス ロジックと対にして、トークンが再利用されないようにします。

ネットワーク盗聴

"ネットワーク盗聴" では、ネットワーク監視ソフトウェアを使用してブラウザと Web サーバーの間で送信されるデータのパケットを追跡する方法を必要とします。ネットワーク盗聴は、アプリケーション固有の機密データの漏えい、ログオン資格情報の取得、または認証 Cookie の捕捉につながるおそれがあります。

脆弱性

ネットワーク盗聴の成功につながる脆弱性には、以下のようなものがあります。

  • 機密性の高いデータを送信するときの暗号化の欠如

  • 暗号化されていないチャンネルでの認証 Cookie の送信

攻撃

ネットワーク盗聴攻撃は、トラフィックを捕捉するためにネットワーク上に配備されているパケット盗聴ツールを使用して実行されます。

対策

ネットワーク盗聴に対抗するには、Secure Sockets Layer (SSL) を利用して、ブラウザと Web サーバーの間に暗号化された通信チャンネルを用意します。ネットワーク経由で資格情報、認証チケット、または機密性の高いアプリケーション データを送信するときは SSL を利用する必要があります。

情報の漏えい

"情報の漏えい" は、攻撃者が例外条件を発生させる方法を探すために Web ページを調査するときに生じます。この方法を攻撃者が有意義に活用できる理由は、一般に例外の詳細が HTML として返されたり、ブラウザで表示されることによって、データベース接続文字列、データベース名、データベース スキーマ情報、SQL ステートメント、およびオペレーティング システムとプラットフォームのバージョンを含むスタック トレースのような非常に有用な情報が明かされるためです。

脆弱性

情報の漏えいにつながる脆弱性には、以下のようなものがあります。

  • 脆弱な例外処理。

  • 未処理の例外詳細がクライアントに送信される。

攻撃

情報の漏えいが生じるおそれのある攻撃は数多くあります。たとえば、以下が挙げられます。

  • バッファ オーバーフロー。

  • 無効な入力を故意に送信。

対策

情報の漏えいを防止するには、以下のようにします。

  • 構造化された例外処理を使用します。

  • クライアントに一般的なエラー ページを返します。

  • 害のない一般的なエラー メッセージを含む既定のリダイレクト ページを使用します。

設計に関する考慮事項

Web ページとコントロールを開発する前に、多くの重要な問題を設計時に考慮する必要があります。主な考慮事項は、以下のとおりです。

  • サーバー側の入力検証を利用します。

  • Web サイトを分割します。

  • リソース アクセスに使用される識別情報を考慮します。

  • 資格情報と認証チケットを保護します。

  • 安全に失敗します。

  • 承認レベルの精度について検討します。

  • Web コントロールとユーザー コントロールを別々のアセンブリに配置します。

  • リソース アクセス コードを別々のアセンブリに配置します。

サーバー側の入力検証を利用する

設計時に、Web ページとコントロールで処理するユーザー入力の各種ソースをすべて指定します。ソースには、バックエンド データ ソースからのデータのほかに、Web ユーザーから受信するフォーム フィールド、クエリ文字列、Cookie も含まれます。Web ユーザーは、明らかにアプリケーションの信頼境界外に存在するので、該当するソースからのすべての入力をサーバーで検証する必要があります。バックエンド データ ソースから受信したデータを確実に信頼できない限り、そのデータをクライアントに送信する前に検証して不適切な部分を削除する必要があります。簡単に回避されるため、実際のソリューションでクライアント側の検証を利用していないことを確認してください。

Web サイトを分割する

Web サイト設計では、公的にアクセス可能な区域と認証済みアクセスを必要とする制限区域とを明確に区別する必要があります。認証済みアクセスを必要とし、クレジット カード番号のような機密性の高いデータを送信する、従来の電子商取引 Web サイトのチェックアウト機能などの制限されたページを保持するには、アプリケーションの仮想ルート ディレクトリの下にある個々のサブディレクトリを使用します。サブディレクトリを個々に使用すれば、SSL を要求してもサイト全体に SSL パフォーマンス オーバーヘッドが生じることなく、さらにセキュリティを強化できます。また、認証 Cookie の送信を HTTPS 接続に制限することによって、セッション ハイジャックのリスクを軽減することもできます。図 10.2 は、一般的な分割を示しています。

公開区域と保護区域に分割された Web サイト

図 10.2
公共区域と保護区域に分割された Web サイト

図 10.2 の制限されたサブフォルダは、インターネット インフォメーション サービス (IIS) で SSL アクセスを要求するように構成されていることに注意してください。Web.config の最初の <authorization> 要素は、すべてのユーザーが公開区域にアクセスできるようにしますが、2 番目の要素は、認証されていないユーザーが保護されたサブフォルダの内容にアクセスできないようにし、ログオンを強制実行します。

認証 Cookie が HTTPS 接続の場合にのみ渡されるように認証 Cookie を制限する方法、および制限されたページと制限されていないページの間を移動する方法の詳細については、このモジュールの「認証」の「移動に絶対 URL を使用する」を参照してください。

リソース アクセスに使用される識別情報を考慮する

既定では、ASP.NET アプリケーションは偽装しないので、最低限の特権を持つ ASPNET プロセス アカウントが、ASP.NET Web アプリケーションの実行とリソース アクセスに使用されます。既定値は推奨された構成です。ただし、リソース アクセスに異なる Windows セキュリティ コンテキストを使用した方がよい場合がいくつかあります。たとえば、以下が挙げられます。

  • 同一サーバーで複数のアプリケーションをホストする場合
    IIS を使用して各アプリケーションを構成すれば、別の匿名インターネット ユーザー アカウントを使用して偽装を可能にすることができます。各アプリケーションには、リソース アクセス用の異なる識別情報が設定されます。この方法の詳細については、モジュール 20 「複数の Web アプリケーションをホストする」を参照してください。

  • 特定の認証要件でリモート リソースにアクセスする場合
    特定のリモート リソース (ファイル共有など) にアクセスする必要があり、使用する特定の Windows アカウントが指定されている場合は、このアカウントをアプリケーションの匿名 Web ユーザー アカウントとして使用できます。その結果、特定のリモート リソースにアクセスする前にプログラムで偽装を利用できます。詳細については、このモジュールで後述する「偽装」を参照してください。

資格情報と認証チケットを保護する

設計時に、資格情報と認証チケットの保護方法を考慮に入れる必要があります。資格情報がネットワークで渡される場合や、構成ファイルなどの固定ストアに存在している間は、資格情報を保護する必要があります。認証チケットは、ハイジャック攻撃を受けやすいため、ネットワーク上で保護する必要があります。これは、暗号化によって解決できます。SSL または IPSec を利用すれば、ネットワーク上で資格情報とチケットを保護できます。また、DPAPI は、構成ファイル内の資格情報を暗号化するための優れたソリューションを提供します。

安全に失敗する

アプリケーションが回復不可能な例外条件で失敗した場合は、そのアプリケーションが安全に失敗し、システムが広く開放されたままになっていないことを確認します。悪質なユーザーにとって貴重な例外詳細をクライアントに送信できないようになっており、代わりに一般的なエラー ページを返すようになっていることを確認してください。また、メソッド エラー コードを利用するのではなく、構造化された例外処理によってエラーを処理するように計画してください。

承認レベルの精度を検討する

サイトの認証された部分で利用する認証の精度を検討します。認証を必要とするようにディレクトリを構成した場合は、そのディレクトリ内のページにすべてのユーザーが同等にアクセスできるようにする必要があるかどうかを考慮します。必要な場合は、個々の <location> 要素内で複数の <authorization> 要素を使用して、識別情報、または一般的に呼び出し側のロール メンバシップに基づいて個々のページにさまざまな認証規則を適用できます。

たとえば、同じディレクトリ内の 2 つのページに Web.config で <allow> と <deny> という別々の要素を設定できます。

Web コントロールとユーザー コントロールを別々のアセンブリに配置する

Web コントロールとユーザー コントロールをそれぞれ独自のアセンブリに挿入した場合は、コード アクセス セキュリティ ポリシーを利用すれば、アセンブリごとにセキュリティを個々に構成できます。これにより、柔軟性の高い管理を行うことができます。つまり、1 つのコントロールの要件を満たすだけのためにすべてのコントロールに拡張アクセス許可を付与する必要はありません。

リソース アクセス コードを別々のアセンブリに配置する

個々のアセンブリを使用して、リソース アクセス コードをページ クラスのイベントハンドラに埋め込むのではなくページ クラスから呼び出します。これにより、コード アクセス セキュリティ ポリシーの柔軟性が大幅に高まります。また、この方法は、特に部分的に信頼できる Web アプリケーションを作成する場合に重要です。詳細については、モジュール 9 「ASP.NET でコード アクセス セキュリティを使用する」を参照してください。

入力検証

入力の型、長さ、形式、または範囲に関する前提に根拠がない場合は、アプリケーションを堅牢にすることはできません。前提に根拠がないことを攻撃者が発見すると、入力検証がセキュリティ問題になる場合があります。その結果、攻撃者は、アプリケーションのセキュリティを侵害するように巧妙に作成した入力を設定できます。ユーザー入力に対する信頼性の欠如は、Web アプリケーションにおける最も一般的かつ致命的な脆弱性の 1 つです。

制限した後、不適切な部分を削除する

入力を制限して開始し、型、長さ、形式、および範囲の検証によって既知の正常なデータを確認します。場合によっては、入力の不適切な部分を削除し、潜在的に悪質な入力を安全にすることも必要です。たとえば、アプリケーションが自由形式入力フィールド (コメント フィールドなど) をサポートしている場合は、特定の "安全な" HTML 要素 (<b>、<i> など) を許可し、他のあらゆる HTML 要素を取り除くことをお勧めします。次の表に、データを制限して不適切な部分を削除する場合に利用できるオプションをまとめています。

表 10.1: データ要件を制限して不適切な部分を削除するためのオプション

要件

オプション

型の確認

.NET Framework 型システム。文字列データの解析、強い型への変換、および FormatExceptions の処理を行います。
正規表現。ASP.NET RegularExpressionValidator コントロールまたは Regex クラスを使用します。

長さの確認

正規表現
String.Length プロパティ

形式の確認

パターン マッチングの正規表現
.NET Framework 型システム

範囲の確認

ASP.NET RangeValidator コントロール (通貨、日付、整数、倍精度、および文字列データをサポート)
型指定されたデータの比較

正規表現

正規表現を利用すれば、有効な文字の範囲の制限、不要な文字の除去、および長さと形式の確認を実行できます。入力形式を制限するには、入力が一致する必要のあるパターンを定義します。ASP.NET は RegularExpressionValidator コントロールを提供し、Regex クラスは System.Text.RegularExpressions 名前空間から利用できます。

検証コントロールを使用する場合は、コントロールが空であれば検証は成功します。必須フィールドの場合は、RequiredFieldValidator を使用します。また、正規表現の検証の実装は、クライアントとサーバーでわずかに異なります。クライアントでは、Microsoft JScript® 開発ソフトウェアの正規表現構文が使用されます。サーバーでは、System.Text.RegularExpressions.Regex 構文が使用されます。JScript 正規表現構文は System.Text.RegularExpressions.Regex 構文のサブセットなので、クライアントとサーバーの両方で同じ結果になるようにJScript 正規表現構文を使用することをお勧めします。

ASP.NET 検証コントロールの全面的な詳細については、.NET Framework のドキュメントを参照してください。

RegularExpressionValidator コントロール

Web フォーム フィールド入力を検証する場合は、RegularExpressionValidator コントロールを使用できます。コントロールを Web フォームにドラッグし、その ValidationExpression、ControlToValidate、ErrorMessage の各プロパティを設定します。

Microsoft Visual Studio® .NET のプロパティ ウィンドウを使用して検証表現を設定できます。あるいは、Page_Load イベント ハンドラでプロパティを動的に設定できます。後者の方法では、ページ上のあらゆるコントロールの正規表現をすべてグループ化できます。

Regex クラス

runat="server" プロパティを指定せずに正規の HTML コントロールを使用 (RegularExpressionValidator コントロールは使用禁止) する場合や、他のソース (クエリ文字列、Cookie など) からの入力を検証する必要がある場合は、個々のアセンブリにおいてページ クラスまたは検証ヘルパー メソッドの Regex クラスを使用できます。このセクションの後半で、いくつかの例を示します。

正規表現のコメント

次の構文で "#" を使用して表現の各コンポーネントにコメントを加える場合、正規表現は、非常に簡単に理解できます。コメントを有効にするには、RegexOptions.IgnorePatternWhitespace の指定も必要です。この指定により、エスケープされていない空白は無視されます。

Regex regex = new Regex(@"
^           # 開始時のアンカー
(?=.*\d)     # 1 桁以上が必要
(?=.*[a-z])  # 小文字が 1 文字必要
(?=.*[A-Z])  # 大文字が1 文字必要
.{8,10}      # 8 ~ 10 文字の長さ
$            # 終了時のアンカー", 
RegexOptions.IgnorePatternWhitespace);

文字列フィールド

文字列フィールド (名前、アドレス、納税者番号など) を検証するには、正規表現を利用して、以下を実行します。

  • 入力文字の許容範囲を制限します。

  • 書式設定規則を適用します。たとえば、パターンベースのフィールド (納税者番号、郵便番号など) には、特定のパターンの入力文字が必要です。

  • 長さを確認します。

名前

次の例は、名前フィールドの検証に使用する RegularExpressionValidator コントロールを示しています。

<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtName" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="nameRegex" runat="server" ControlToValidate="txtName" ValidationExpression="^[a-zA-Z'.`-´\s]{1,40}$"
							ErrorMessage="Invalid name"></asp:RegularExpressionValidator>
</form>

前述の検証表現では、入力名フィールドが英字 (小文字と大文字)、O'Dell のような名前の 1 つのアポストロフィ、およびドット文字に制限されます。さらに、フィールド長が 40 文字に制限されます。

社会保険番号

次の例は、米国の社会保険番号フォーム フィールドの検証に使用されている RegularExpressionValidator コントロール向けに生成される HTML コードを示しています。

<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtSSN" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="ssnRegex" runat="server" ErrorMessage="Invalid social security number" ValidationExpression="\d{3}-\d{2}-\d{4}"
							ControlToValidate="txtSSN"></asp:RegularExpressionValidator>
</form>

前述の検証表現は、Visual Studio .NET に用意されている標準的な表現の 1 つです。この式では、指定された入力フィールドの形式、およびその型と長さが検証されます。入力は、必ず 3 桁の数字とその後にダッシュ、次に 2 桁の数字とその後にダッシュ、さらに 4 桁の数字から成ります。

サーバー コントロール (検証コントロールは禁止) を使用していない場合や、フォーム フィールド以外のソースからの入力を検証する必要がある場合は、メソッド コードで System.Text.RegularExpression.Regex クラスを使用できます。次の例は、検証コントロールを使用するのではなく、ページ クラスで静的 Regex.IsMatch メソッドを直接使用して同じフィールドを検証する方法を示しています。

if (!Regex.IsMatch(txtSSN.Text, @"^\d{3}-\d{2}-\d{4}$"))
{
// 無効な社会保険番号
}

日付フィールド

同じ .NET Framework 型を持つ入力フィールドの型は、.NET Framework 型システムで確認できます。たとえば、日付を検証する場合、入力データに互換性がなければ、次のように入力値を型 System.DateTime の変数に変換して、その結果の形式例外を処理できます。

try
{
DateTime dt = DateTime.Parse(txtDate.Text).Date;
}
// 型変換が失敗すると、FormatException がスローされます。
catch( FormatException ex )
{
// 無効な日付メッセージを呼び出し側に返します。
}

形式と型の確認のほかに、日付フィールドの範囲の確認を必要とする場合があります。この確認は、次のように DateTime 変数を使用して簡単に実行されます。

// 例外処理は省略
DateTime dt = DateTime.Parse(txtDate.Text).Date;
// 日付は今日以前にする必要があります。
if ( dt > DateTime.Now.Date )
throw new ArgumentException("Date must be in the past");

数値フィールド

数値データ (有効期間など) を検証する必要がある場合は、int 型を使用して型確認を実行します。文字列入力を整数に変換する場合は、次のように、Int32.Parse または Convert.ToIn32 を使用すれば、無効なデータ型で発生するあらゆる FormatException を処理できます。

try
{
int i = Int32.Parse(txtAge.Text);
  . . .
}
catch( FormatException)
{
 . . .
}
範囲の確認

場合によっては、入力データが定義済みの範囲に含まれていることを検証する必要があります。次のコードでは、ASP.NET RangeValidator コントロールを使用して、入力を 0 ~ 255 までの数値に制限しています。この例では、RequiredFieldValidator も使用しています。RequiredFieldValidator を使用しない場合は、もう一方の検証コントロールは空白入力を受け入れます。

<form id="WebForm3" method="post" runat="server">
<asp:TextBox id="txtNumber" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator id="rangeRegex" runat="server" ErrorMessage="Please enter a number between 0 and 255"
							ControlToValidate="txtNumber" style="LEFT:10px; POSITION:absolute; TOP:47px"></asp:RequiredFieldValidator>
<asp:RangeValidator id="RangeValidator1" runat="server" ErrorMessage="Please enter a number between 0 and 255"
							ControlToValidate="TextBox1" Type="Integer" MinimumValue="0" MaximumValue="255"
							style="LEFT:10px; POSITION:absolute; TOP:47px"></asp:RangeValidator>
<asp:Button id="Button1" style="LEFT:10px; POSITION:absolute; TOP:100px" runat="server" Text="Button"></asp:Button>
</form>

次の例は、Regex クラスを使用して範囲を検証する方法を示しています。

try
{
// 入力が範囲外の場合は例外が発生します。
int i = Convert.ToInt32(sInput);
if ((0 <= i && i <= 255) == true)
  {
// データが有効なので、この数値を使用します。
  }
}
catch( FormatException )
{
  . . .
}

入力の不適切な部分を削除する

不適切な部分を削除するのは、潜在的に悪質なデータを安全にするためです。この機能は、許容入力範囲の確認だけでは入力が安全であることを保証できない場合に役立つ可能性があります。この機能には、ユーザー指定の文字列またはエスケープされた値がリテラルと見なされるように、それらの末尾から NULL を除去する機能が含まれています。入力の不適切な部分を削除して特定の入力文字の変換または除去を行う必要がある場合は、Regex.Replace を使用します。

注: この方法は、多層防御に利用してください。常に、入力を既知の "正常な" 値のセットに制限することから始めます。

次のコードでは、<>\"'%;()& などの潜在的に安全でない各種の文字を取り除いています。

private string SanitizeInput(string input)
{
Regex badCharReplace = new Regex(@"([<>""'%;()&])");
string goodChars = badCharReplace.Replace(input, "");
return goodChars;
}

コメント フィールドのような自由形式入力フィールドの不適切な部分を削除する方法の詳細については、このモジュールで後述する「クロスサイト スクリプティング」の「自由形式入力の不適切な部分を削除する」を参照してください。

HTML コントロールを検証する

サーバー コントロール、つまり runat="server" 属性を持つコントロールを使用せずに、正規の HTML コントロールを使用する場合は、ASP.NET 検証コントロールを使用できません。代わりに、Web ページの内容を検証するには、次のように、Page_Load イベント ハンドラで正規表現を利用します。

using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// IsPostBack は、runat="server を指定したサーバー フォームです。
// だけに適用される
if ( Request.RequestType == "POST" ) // 非サーバー フォーム
  {
// 指定された電子メール アドレスを検証する
if( !Regex.Match(Request.Form["email"], 
@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$",
RegexOptions.None).Success) 
    {
// 無効な電子メール アドレス
    }
// 指定された名前を検証します。
if ( !RegEx.Match(Request.Form["name"],
@"^[A-Za-z'\- ]$",
RegexOptions.None).Success) 
    {
// 無効な名前
    }
  }
}

データ アクセスに使用される入力を検証する

ユーザー入力に基づいて動的 SQL クエリを生成している場合は、SQL インジェクション攻撃により、データベースで実行できる悪質な SQL コマンドが注入されるおそれがあります。一般的な Web ベースのデータ アクセス シナリオでは、以下の多層防御戦略を取り入れることができます。

  • 正規表現を使用して、ページ クラス内の入力を制限します。

  • 入力の不適切な部分を削除するか、入力を拒否します。多層防御の場合は、ヘルパー メソッドを使用して、NULL 文字または他の既知の不正な文字を除去することも可能です。

  • SQL クエリで使用されるデータに対して型と長さの確認が確実に実行されるように、データ アクセス用のパラメータ化されたストアド プロシージャを使用します。

データ アクセス用のパラメータ、および安全なデータ アクセス コードの作成方法の詳細については、モジュール 14 「セキュリティ保護されたデータ アクセスを構築する」を参照してください。

ファイル I/O に使用される入力を検証する

一般に、呼び出し側からのファイル入力またはパス入力を受け入れるコードは作成しないようにする必要があります。代わりに、データの読み取り時や書き込み時に固定的なファイル名と場所を使用します。このようにすれば、コードから任意のファイルへのアクセスを強制されることはなくなります。また、正規化のバグに対するコードの脆弱性をなくすこともできます。

入力ファイル名を受け入れる必要がある場合は、2 つの大きな課題があります。1 つ目は、受け入れたファイル パスや名前が有効なファイル システム名になっているかどうかです。2 つ目は、パスがアプリケーションのコンテキストで有効であるかどうかです。たとえば、パスがアプリケーションの仮想ディレクトリ ルートの下にあるでしょうか。

ファイル名を正規化するには、System.IO.Path.GetFullPath を使用します。ファイル パスがアプリケーションのコンテキストにおいて有効であることを確認する場合は、.NET コード アクセス セキュリティを利用して、特定のディレクトリからのファイルだけにアクセスできるようにコードに正確な FileIOPermission を付与できます。詳細については、モジュール 7 「セキュリティ保護されたアセンブリを構築する」およびモジュール 8「コード アクセス セキュリティの実践」の「ファイル I/O」を参照してください。

MapPath を使用する

MapPath を使用して指定された仮想パスをサーバー上の物理パスに対応付ける場合は、次のように、クロス アプリケーション マッピングを防止できるように "ブール" パラメータを受け入れる Request.MapPath のオーバーロードを利用します。

try
{
string mappedPath = Request.MapPath( inputPath.Text, 
Request.ApplicationPath, false);
}
catch (HttpException)
{
// クロス アプリケーションマッピングが試行されます。
}	

最後の false パラメータによって、クロス アプリケーション マッピングが防止されます。つまり、ユーザーは、アプリケーションの仮想ディレクトリ階層外に移動するために ".." を含むパスを指定できないということです。指定しようとしても、型 HttpException の例外が発生します。

注: サーバー コントロールでは、ファイルを読み取るために Control.MapPathSecure メソッドを使用できます。このメソッドでは、コード アクセス セキュリティ ポリシーによって呼び出し側のコードが完全に信頼できる状態でなければなりません。そうでなければ、HttpException がスローされます。詳細については、.NET Framework SDK ドキュメントの「Control.MapPathSecure」を参照してください。

一般的な正規表現

Visual Studio .NET には、実用的な特定の正規表現が用意されています。こうした正規表現にアクセスするには、RegularExpresssionValidator コントロールを Web フォームに追加し、コントロールの "式" プロパティ フィールドの省略ボタンをクリックします。次の表は、一般に Web ページ フィールドに使用される、さらに実用的な表現をいくつか示しています。

表 10.2: 実用的な正規表現フィールド

フィールド

表現

形式のサンプル

説明

名前

[a-zA-Z'`-´\s]{1,40}

John DoeO'Dell

名前を検証します。大文字と小文字、および名前によく使われるいくつかの特殊文字を 40 文字まで使用できます。このリストは変更できます。

数字

^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$

(425)-555-0123
425-555-0123
425 555 0123

米国の電話番号を検証します。

電子メール

\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

someone@example.com

電子メール アドレスを検証します。

URL

^(http|https|ftp)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*$

 

URL を検証します。

郵便番号

^(\d{5}-\d{4}|\d{5}|\d{9})$|^([a-zA-Z]\d[a-zA-Z] \d[a-zA-Z]\d)$

 

米国の 5 桁または 9 桁の郵便番号を検証します。

パスワード

^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$

 

強固なパスワードを検証します。8 ~ 10 文字を指定する必要があります。特殊文字は使用せずに、大文字、小文字、および数字を組み合わせる必要があります。

非負整数

\d+

0986

0 より大きい整数を検証します。

通貨 (0 以上)

"\d+(\.\d\d)?"

 

正の貨幣額を検証します。小数点以下 2 桁を必要とします。

通貨 (正または負)

"(-)?\d+(\.\d\d)?"

 

正または負の貨幣額を検証します。小数点以下 2 桁を必要とします。

クロスサイト スクリプティング

XSS 攻撃では、Web ページ検証の脆弱性を悪用するために、クライアント側のスクリプト コードが注入されます。その後、このコードは疑いを持たないユーザーに返送され、ブラウザで実行されます。ブラウザは、信頼できるサイトからスクリプト コードをダウンロードするため、コードが正当でないことを識別する方法がブラウザに用意されておらず、Internet Explorer のセキュリティ ゾーンでは防御できません。XSS 攻撃は、HTTP または HTTPS (SSL) 接続でも機能します。最も深刻な脆弱性が検出されるのは、攻撃者が信頼できるサイトへのアクセスを提供する認証 Cookie を取得するスクリプトを作成し、それを攻撃者が知っている Web アドレスに送信する場合です。この脆弱性により、攻撃者は、正当なユーザーの識別情報を偽装して Web サイトに不法にアクセスできます。

XSS 攻撃を防ぐには、以下の対策を実施してください。

  • 入力の検証

  • 出力のエンコード

入力を検証する

このモジュールで以前に説明した各種テクノロジを駆使して型、長さ、形式、範囲について、アプリケーションの信頼境界外から受信するあらゆる入力を検証します。

出力をエンコードする

テキスト出力を Web ページに書き込み、そのテキストに HTML 特殊文字 (<、>、& など) が含まれていないことがはっきりしない場合は、HttpUtility.HtmlEncode メソッドを使用してそのテキストを必ず前処理してください。ユーザー入力、データベース、またはローカル ファイルからのテキストであっても、この作業を行ってください。同様に、HttpUtility.UrlEncode を使用して URL 文字列をエンコードします。

HtmlEncode メソッドは、HTML で特殊な意味を持つ文字をそれぞれの文字を表す HTML 変数に置換します。たとえば、< は < に置き換えられ、" は " に置き換えられます。データをエンコードしておけば、ブラウザでコードとして実行されることはありません。代わりに、そのデータは害のない HTML として表示されます。

Response.Write(HttpUtility.HtmlEncode(Request.Form["name"]));
データ連結コントロール

データ連結 Web コントロールでは、出力をエンコードしません。出力をエンコードする唯一のコントロールは TextBox コントロールで、しかもその TextMode プロパティが MultiLine に設定されている場合です。他のコントロールを悪質な XSS コードを持つデータにバインドすると、そのコードはクライアントで実行されます。その結果、データベースからデータを取得し、おそらくデータベースが他のアプリケーションと共有されているためにデータが有効であることがはっきりしない場合は、そのデータをクライアントに渡す前にデータをエンコードしてください。

自由形式入力の不適切な部分を削除する

Web ページに自由形式のテキスト ボックス ("コメント" フィールドなど) が含まれており、そこに特定の安全な HTML 要素 (<b>、<i> など) を許可したい場合、この処理を正しく行うには、次のように、最初に HtmlEncode で前処理した後、許可されている要素のエンコードを部分的に削除します。

StringBuilder sb = new StringBuilder( HttpUtility.HtmlEncode(userInput) ) ;
sb.Replace("<b>", "<b>");
sb.Replace("</b>", "</b>");
sb.Replace("<i>", "<i>");
sb.Replace("</i>", "</I>");
Response.Write(sb.ToString());

多層防御対策

既に説明した技法のほかに、以下の多層防御向け対策によって XSS を防止します。

  • 正しい文字エンコードを設定します。

  • ASP.NET バージョン 1.1 の validateRequest オプションを使用します。

  • URLScan を Web サーバーにインストールします。

  • HttpOnly Cookie オプションを使用します。

  • <frame> セキュリティ属性を使用します。

  • innerText プロパティを使用します。

正しい文字エンコードを設定する

Web ページに対して有効なデータを正しく制限するには、入力データの表現方法を制限することが重要です。このようにすれば、悪質なユーザーは、正規化およびマルチバイト エスケープ シーケンスを使用して入力検証ルーチンに仕掛けを施すことができなくなります。

ASP.NET では、Web.config で <globalization> 要素を使用することにより、ページ レベルまたは アプリケーション レベルで文字セットを指定できます。この 2 つの方法を、ISO-8859-1 文字エンコードを使用して以下に示します。このエンコードは、HTML と HTTP の初期バージョンの既定値です。

ページ レベルで文字エンコードを設定するには、次のように <meta> 要素または ResponseEncoding ページ レベル属性を使用します。

<meta http-equiv="Content Type" 
content="text/html; charset=ISO-8859-1" />

または

<% @ Page ResponseEncoding="ISO-8859-1" %>

Web.config で文字エンコードを設定するには、次の構成を使用します。

<configuration>
<system.web>
							<globalization requestEncoding="ISO-8859-1" responseEncoding="ISO-8859-1" />
						</system.web>
</configuration>

Unicode 文字を検証する

ページ内の Unicode 文字を検証するには、次のコードを使用します。

using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// 名前には、1 ~ 40 桁の英数字、および
// 必要に応じて D'Angelo のような名前に対する
// 特殊文字 '`´ を指定します。
if (!Regex.IsMatch(Request.Form["name"], @"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
throw new ArgumentException("Invalid name parameter");
// 個別に正規表現を利用して他のパラメータを検証します。
  . . .
}

以下に、前出のコードで使用されている正規表現を説明します。

  • {<name>}: 指定された Unicode 文字クラスを示します。

  • \p{<name>}: {<name>} の示す指定された文字クラスの任意の文字に対応します。

  • {L}: 左から右に検出を実行します。

  • {Lu}: 大文字の検出を実行します。

  • {Ll}: 小文字の検出を実行します。

  • {Zs}: 区切り文字と空白文字を検出します。

  • {1,40}: 1 ~ 40 文字までを表します。

  • {Mn}: 記号および空白でない文字を検出します。

  • {Zs}: 区切り文字と空白文字を検出します。

  • *: 0 文字以上の検出を示します。

  • $: この位置で参照を終了することを表します。

ASP.NET validateRequest オプションを使用する

validateRequest 属性は、.NET Framework バージョン 1.1 の機能です。この属性は、Machine.config における <pages> 要素の既定値によって True に設定されます。この設定により、ASP.NET で、ブラウザから受信したすべてのデータに悪質な入力 (<script> 要素を含む入力など) が含まれていないか調査されます。ASP.NET では、HTML フォーム フィールド、Cookie、およびクエリ文字列から受信した入力が調べられます。.NET Framework バージョン 1.0 には同等の機能はありませんが、IIS URLScan ISAPI (Internet Server Application Programming Interface) フィルタで類似ジョブを実行できます。また、次のように、@ Page タグを使用して設定を各ページに適用することもできます。

<% @ Page validateRequest="True" %>
URLScan を Web サーバーにインストールする

URLScan は、IISLockdown ツールを実行するときにインストールされる ISAPI フィルタです。このフィルタは、潜在的に悪質な入力を拒否することで XSS 攻撃の脅威を軽減するのに役立ちます。IISLockdown と URLScan の詳細については、モジュール 16 「Web サーバーをセキュリティ保護する」を参照してください。

注: Windows Server 2003 の IIS 6.0 には、組み込みの URLScan に相当する機能が用意されています。

HttpOnly Cookie オプションを使用する

Internet Explorer 6 Service Pack 1 は新しい HttpOnly Cookie 属性をサポートします。この属性により、クライアント側スクリプトで document.cookie プロパティから Cookie にアクセスできなくなります。代わりに、空の文字列が返されます。ただし、ユーザーが現在のドメインの Web サイトを参照するときは必ず、これまでどおり Cookie がサーバーに送信されます。

注: HttpOnly Cookie 属性をサポートしていない Web ブラウザは、Cookie を無視するか、属性を無視します。つまり、依然として XSS 攻撃を受けるおそれがあるということです。

System.Net.Cookie クラスは、現在 HttpOnly プロパティをサポートしていません。Cookie HttpOnly 属性を追加するには、ISAPI フィルタを使用する必要があります。あるいは、マネージ コード ソリューションが必要な場合は、Global.asax でアプリケーションの Application_EndRequest イベント ハンドラに次のコードを追加します。

protected void Application_EndRequest(Object sender, EventArgs e) 
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies) 
  {
// フォーム認証 Cookie に HttpOnly 属性を設定します。
// コレクション内のすべての Cookie に属性を設定するために、この確認をスキップします。
if (sCookie.Equals(authCookie))
    { 
// HttpOnly をCookie ヘッダーに強制的に追加します。
Response.Cookies[sCookie].Path += ";HttpOnly";
    }
  }
}

注: .NET Framework の今後のバージョンでは、Cookie クラスに HttpOnly プロパティを装備する可能性があります。

<frame> セキュリティ属性を使用する

Internet Explorer 6 以降では、<frame> と <iframe> の要素に対する security 属性を新たにサポートします。この security 属性を使用すれば、ユーザーの Internet Explorer の制限付きサイト セキュリティ ゾーン設定を個々の frame または iframe に適用できます。既定では、制限付きサイトゾーンは、スクリプト実行をサポートしていません。security 属性を使用する場合は、以下に示すように "restricted" に設定する必要があります。

<frame security="restricted" src="http://www.somesite.com/somepage.htm"></frame>
innerText プロパティを使用する

信頼されていない入力でページを作成する場合は、innerHTML ではなく innerText プロパティを使用します。innerText プロパティは、コンテンツを安全にし、スクリプトが実行されないようにします。

認証

認証が脆弱な場合は、なりすましの脅威が高まります。ユーザーのログオン資格情報が攻撃者に渡ると、攻撃者は、ユーザーの識別情報を偽装してアプリケーションにアクセスできるようになります。攻撃者は、そのアプリケーションでユーザーのすべての特権を共有します。資格情報は、ネットワーク経由で渡されるときやアプリケーションのユーザー ストアなどに保持されている間、保護されている必要があります。最初のログオン後にアプリケーションに対する認証済み識別情報を表す認証 Cookie も、セッション ハイジャックや Cookie リプレイ攻撃のリスクを軽減するために保護されている必要があります。

フォーム認証

フォーム認証を利用するアプリケーションには、セッション ハイジャックと Cookie リプレイ攻撃の脅威は特に重要です。SQL インジェクションに対して脆弱にならないようにユーザー指定の資格情報を使用してデータベースを照会する場合は、特に注意を払う必要があります。さらに、なりすましを防ぐために、ユーザー ストアがセキュリティ保護されており、強固なパスワードが設定されていることを確認する必要があります。

次のコードは、Web.config での "セキュリティ保護された" フォーム認証構成を示しています。

<forms loginUrl="Restricted\login.aspx"  SSL で保護されたフォルダ内のログイン ページ
protection="All"                  プライバシーと整合性
requireSSL="true"                 http で Cookie を送信しないようにする
timeout="10"                      セッション有効期間の制限
name="AppNameCookie"              アプリケーションごとの一意な名前
path="/FormsAuth"                    およびパス
slidingExpiration="true" >        セッション有効期間を変更する
</forms>

以下の推奨事項は、セキュリティ保護されたフォーム認証ソリューションを構築するのに役立ちます。

  • Web サイトを分割します。

  • 制限されたページを SSL でセキュリティ保護します。

  • URL 認証を利用します。

  • 認証 Cookie をセキュリティ保護します。

  • 移動に絶対 URL を使用します。

  • セキュリティ保護された資格情報管理を利用します。

Web サイトを分割する

サイト設計で、認証済みアクセスを必要とするセキュリティ保護されたページが、匿名でアクセスできるページとは別のサブディレクトリに格納されていることを確認します。図 10.3 は、Visual Studio .NET ソリューション エクスプローラ ウィンドウでの一般的な配列を示しています。フォーム ログイン ページが、別々のサブディレクトリにある他の制限されたページと共に格納されていることに注意してください。

認証済みアクセスを必要とする制限されたページ用のサブディレクトリ

図 10.3
認証済みアクセスを必要とする制限されたページ用のサブディレクトリ

注: 匿名ページからセキュリティ保護されたページに転送するためにアプリケーションで Server.Transfer を使用している場合、.NET Framework バージョン 1.1 以前では、認証検査を回避するので、Server.Transfer を使用するコードでセキュリティ保護されたディレクトリに転送していないことを確認する必要があります。

制限されたページを SSL でセキュリティ保護する

SSL を利用してログイン フォームから送信されるログオン資格情報を確実に保護し、制限されたページに対する以降の要求に認証 Cookie を確実に渡すために、SSL を要求するように IIS のセキュリティ保護されたフォルダを構成します。これにより、IIS メタベースにおいてフォルダの AccessSSL=true 属性が設定されます。要求 URL で https が使用されている場合に限り、セキュリティ保護されたフォルダ内のページの要求が成功します。

SSL の場合は、サーバー証明書が Web サーバーにインストールされている必要があります。詳細については、Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「Web サーバー上で SSL を設定する方法」(http://www.microsoft.com/japan/msdn/net/security/SecNetHT16.aspx) を参照してください。

URL 認証を利用する

公開ページへの匿名アクセスを許可するには、次の <authorization> 要素を使用します。

<system.web>
<!-- 仮想ディレクトリ ルート フォルダには一般的なページが格納されています。
認証されていないユーザーは一般的なページを表示できますが、
それを SSL でセキュリティ保護する必要はありません。 -->
<authorization>
						<allow users="*" />
					</authorization>
</system.web>

Web.config の <location> 要素において次の <authorization> 要素を使用して、認証されていないユーザーのアクセスを拒否し、<forms> 要素で指定されたログイン ページに強制的にリダイレクトします。

<!-- 制限されたフォルダは、認証済みアクセスと SSL アクセス専用です。 -->
<location path="Secure">
						<system.web>
							<authorization>
								<deny users="?" />
							</authorization>
						</system.web>
					</location>

認証 Cookie をセキュリティ保護する

セッション ハイジャックや Cookie リプレイ攻撃を防ぐには、Cookie をセキュリティ保護するために必ず HTTPS プロトコルを使用して SSL 接続でのみ Cookie を渡します。さらにリスクを軽減するために、Cookie を暗号化した後にクライアントに送信し、Cookie が有効である期間を制限します。認証 Cookie をセキュリティ保護するには、以下のようにします。

  • 認証 Cookie を HTTPS 接続に限定。

  • Cookie を暗号化します。

  • Cookie の有効期間を制限します。

  • 固定的な有効期限の使用を考慮します。

  • 認証 Cookie を保持しません。

  • 認証 Cookie と個人情報 Cookie を使い分けます。

  • 異なる Cookie 名とパスを使用します。

認証 Cookie を HTTPS 接続に限定する

Cookie では、ブラウザがサーバーに Cookie を返送する必要があるかどうかを決定する "Secure" プロパティをサポートしています。Secure プロパティ セットと共に、Cookie は、HTTPS URL を使用して要求されるセキュリティ保護されたページにのみブラウザから送信されます。

.NET Framework バージョン 1.1 を使用している場合は、次のように <forms> 要素で requireSSL="true" を使用して Secure プロパティを設定します。

<forms loginUrl="Secure\Login.aspx"
requireSSL="true" . . . />

.NET Framework バージョン 1.0 を使用している場合は、Global.asax 内の Application_EndRequest イベント ハンドラで次のコードを使用して Secure プロパティを手動で設定します。

protected void Application_EndRequest(Object sender, EventArgs e) 
{
string authCookie = FormsAuthentication.FormsCookieName;

foreach (string sCookie in Response.Cookies) 
  {
if (sCookie.Equals(authCookie))
    { 
// Cookieをセキュリティ保護するように設定します。ブラウザは、Cookie を https で要求されたページ
// だけに送信します。
Response.Cookies[sCookie].Secure = true;
    }
  }
}
Cookie を暗号化する

SSL を使用している場合でも Cookie の内容を暗号化します。このようにすれば、攻撃者は、XSS に対する脆弱さを介して Cookie を盗んだとしても、Cookie の表示や変更ができなくなります。ただし、この場合、攻撃者は引き続き Cookie を使用してアプリケーションにアクセスできます。このようなリスクを軽減する最善の方法は、このモジュールで前述の「クロスサイト スクリプティング」で説明した XSS 攻撃を防ぐ適切な対策を導入し、次の推奨事項で説明するように、Cookie の有効期限を制限することです。

Cookie のプライバシーと整合性を提供するには、次のように <forms> 要素に protection 属性を設定します。

<forms protection="All"    プライバシーと整合性
Cookie の有効期限を制限する

Cookie の有効期限を制限し、攻撃者が捕捉した Cookie を使用してアプリケーションに偽装アクセスできる期間を短縮します。

<forms timeout="10"                Cookie の有効期間の短縮 (10 分)
固定的な有効期限の使用を考慮する

各 Web 要求の後に Cookie の有効期間を再設定するのではなく、<forms> 要素に slidingExpiration="false" を設定して Cookie の有効期間を固定することを考慮します。この方法は、Cookie の保護に SSL を利用していない場合に特に重要です。

注: この機能は、.NET Framework バージョン 1.1 で利用できます。

認証 Cookie を保持しない

認証 Cookie を保持しない理由は、認証 Cookie はユーザーのプロファイルに保存され、攻撃者がユーザーのコンピュータに物理的にアクセスできる場合は盗まれるおそれがあるためです。次のように FormsAuthenticationTicket を作成するときに非持続 Cookie を指定できます。

FormsAuthenticationTicket ticket = 
new FormsAuthenticationTicket(
1,                           // バージョン
Context.User.Identity.Name,  // ユーザー名
DateTime.Now,                // 発行日時
DateTime.Now.AddMinutes(15), // 15 分ごとに期限切れになります。
false,                       // Cookie を保持しません。
roleStr );                   // ユーザーのロール
認証 Cookie と個人情報 Cookie を使い分ける

ユーザー固有の設定や重要でないデータを格納する個人情報 Cookie と認証 Cookie を使い分けます。個人情報 Cookie は盗まれてもセキュリティの脅威にならない場合もありますが、攻撃者が盗んだ認証 Cookie を使用すれば、アプリケーションにアクセスできるようになります。

異なる Cookie 名とパスを使用する

<forms> 要素に固有の name と path の属性値を使用します。固有の名前にしておけば、同一サーバーで複数のアプリケーションをホストする場合に問題を生じないようにすることができます。たとえば、区別できる名前を使用していなければ、あるアプリケーションで認証されているユーザーが、そのアプリケーションのログオン ページにリダイレクトされずに別のアプリケーションに要求することになる場合があります。

詳細については、Microsoft サポート技術情報 313116「[PRB] フォーム認証要求が loginUrl ページにリダイレクトされない」および 310415 「PRB: モバイル フォーム認証 と別の Web アプリケーション」を参照してください。

移動に絶対 URL を使用する

サイトの公開区域と制限区域の間 (つまり、HTTP ページと HTTPS ページの間) の移動が問題になる理由は、リダイレクトで、ターゲット ページではなく現在のページのプロトコル (HTTPS または HTTP) が必ず使用されるためです。

ユーザーが、SSL でセキュリティ保護されているディレクトリ内のページにログオンして参照すると、"..\publicpage.aspx" のような相対リンクや HTTP ページへのリダイレクトにより、ページが https プロトコルを使用して処理されるため、無用なパフォーマンス オーバーヘッドが生じます。これを回避するには、HTTPS ページから HTTP ページにリダイレクトするときに "http://servername/appname/publicpage.aspx" のような絶対リンクを使用します。

同様に、サイトの公開区域からセキュリティ保護されたページ (ログイン ページなど) にリダイレクトするときは、restricted/login.aspx のような相対パスではなく、"https://servername/appname/secure/login.aspx" のような絶対 HTTPS パスを使用する必要があります。たとえば、Web ページにログオン ボタンが用意されている場合は、次のコードを使用してセキュリティ保護されたログイン ページにリダイレクトします。

private void btnLogon_Click( object sender, System.EventArgs e )
{
// サーバー名と仮想ディレクトリ名を使用して絶対パスを形成します。
string serverName = 
HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);
string vdirName = Request.ApplicationPath;
Response.Redirect("https://" + serverName + vdirName + 
"/Restricted/Login.aspx");
}

セキュリティ保護された資格情報管理を利用する

なりすましは、アプリケーションに対する最も一般的な認証関連の脅威の 1 つです。なりすましは、攻撃者が別のユーザーに見せかけてアプリケーションにアクセスするときに発生します。なりすましを実行する 1 つの方法は、セッション Cookie を強奪することですが、前に説明したように認証 Cookie をセキュリティ保護している場合は、このリスクが大幅に低下します。さらに、セキュリティ保護された資格情報管理およびセキュリティ保護されたユーザー ストアを構築し、ブルート フォース パスワード攻撃、辞書攻撃、SQL インジェクション攻撃などによってもたらされるリスクを軽減する必要があります。

以下の推奨事項は、リスクを削減するのに役立ちます。

  • パスワードに一方向性ハッシュを使用します。

  • 強固なパスワードを指定します。

  • SQL インジェクションを防止します。

パスワードに一方向性ハッシュを使用する

ユーザー ストアが SQL Server である場合は、ランダムな salt 値が追加された一方向性パスワード ダイジェスト (ハッシュ値) を格納します。salt 値を追加すれば、ブルート フォース パスワード クラッキング試行 (辞書攻撃など) のリスクが軽減されます。ダイジェストによる方法では、実際にパスワードを格納することはありません。代わりに、ユーザーからパスワードを取得し、それを検証するために、ダイジェストを再計算して格納された値と比較します。

強固なパスワードを指定する

ユーザー パスワードが強固なパスワード ガイドラインに確実に従うように正規表現を利用します。次の正規表現を利用すれば、長さが 8 ~ 10 文字で、大文字、小文字、数字、および特殊文字が混在するパスワードを指定できます。これにより、辞書攻撃のリスクがさらに軽減されます。

private bool IsStrongPassword( string password )
{
}
SQL インジェクションを防止する

フォーム認証は、ユーザー指定のログオン資格情報がデータベースの照会に使用される方式のため、その脆弱性が SQL インジェクション攻撃につながる傾向があります。このリスクを軽減するには、以下のようにします。

  • 指定された資格情報を徹底的に検証します。正規表現を使用して資格情報に SQL 文字が含まれないようにします。

  • パラメータ化されたストアド プロシージャを使用してユーザー ストア データベースにアクセスします。

  • 制限され、最小限の特権が与えられている、データベースへのログインを利用します。

SQL インジェクションを防ぐ方法の詳細については、モジュール 14 「セキュリティ保護されたデータ アクセスを構築する」を参照してください。

承認

承認を利用すれば、ディレクトリ、個々の Web ページ、ページ クラス、およびメソッドへのアクセスを制御できます。必要な場合は、承認ロジックをメソッド コードに追加することもできます。Web ページやコントロールに承認を組み込む場合は、以下の推奨事項を考慮します。

  • ページやディレクトリのアクセス制御に備えて URL 承認を利用します。

  • Windows 認証でファイル承認を利用します。

  • クラスやメソッドでプリンシパル要求を利用します。

  • きめの細かい承認に備えて明確なロール確認を利用します。

ページやディレクトリのアクセス制御に備えて URL 承認を利用する

ページ レベルやディレクトリ レベルのアクセス制御では、<authorization> 要素で構成される URL 承認を利用します。アクセスを特定のファイルまたはディレクトリに限定するには、<location> 要素内に <authorization> 要素を挿入します。

詳細については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」の「認証」を参照してください。

Windows 認証でファイル承認を利用する

Windows 認証に備えて ASP.NET を構成する場合は、FileAuthorizationModule は ASP.NET のファイルの種類に対するあらゆる要求を確認します。確認対象には、ASP.NET ページ ファイル (.aspx)、ユーザー コントロール (.ascx)、そのほかに IIS で ASP.NET ISAPI フィルタに対応付けられたファイルの種類などが含まれています。

FileAuthorizationModule を構成するには、ASP.NET ファイルに適切な Windows アクセス制御リスト (ACL) を設定します。

クラスやメソッドでプリンシパルな要求を利用する

プリンシパル アクセス許可要求により、呼び出し側の識別情報およびロール メンバシップに基づいて承認を決定できます。呼び出し側の識別情報やロール メンバシップは、現在の Web 要求 (HttpContext.User を介してアクセス) に関連付けられているプリンシパル オブジェクトによって保持されます。クラスやメソッドにアクセス制御を設定するには、次のように宣言型セキュリティ属性を使用します。

// 宣言型構文
[PrincipalPermission(SecurityAction.Demand, 
Role=@"DomainName\WindowsGroup")]
public void SomeRestrictedMethod()
{
}

きめの細かい承認に備えて明確なロール確認を利用する

宣言型のセキュリティ確認により、ユーザーがクラスへのアクセスや特定のメソッドの呼び出しを実行できなくなります。承認を決定するためにメソッド内にさらにロジックを追加する必要がある場合は、必須のプリンシパル アクセス許可要求または IPrincipal.IsInRole を使用した明確なロール確認を利用します。こうした方法により、さらに実行時変数を使用して承認決定の微調整を行うことができます。次の例は、必須のプリンシパル アクセス許可要求を利用したコードを示しています。

// 必須の構文
public void SomeRestrictedMethod()
{
// 指定された Windows グループのメンバである呼び出し側だけが
// アクセスを許可されます。
PrincipalPermission permCheck = new PrincipalPermission(
null, @"DomainName\WindowsGroup");
permCheck.Demand();
// 制限された一部の処理 (省略)
}

次の例は、IPrincipal.IsInRole を使用したコードを示しています。

public void TransferMoney( string fromAccount,
string toAccount, double amount}
{
// 認証されたユーザーを現在の HTTP コンテキストから抽出します。
// .aspx ページ (または .asmx) を使用している場合、User 変数は
// HttpContext.Current.User に等しくなります。
WindowsPrincipal authenticatedUser = User as WindowsPrincipal;
if (null != authenticatedUser)
  {
// 注: 認証されたユーザーのユーザー名を取得するには、
// 次のコード行を使用する
// string username = authenticatedUser.Identity.Name;
// amount がしきい値を上回る場合、管理者の承認を必要とします。
if (amount > thresholdValue) {
// ロール確認を実行します。
if (authenticatedUser.IsInRole(@"DomainName\Manager") )
      {
// 転送の続行を承諾します。
      }
else
      {
throw new Exception("Unauthorized funds transfer");
      }
    }
else
    {
      . . .
    }
  }
}

また、さまざまなロールに基づいて呼び出し側を許可するメソッドを用意してもかまいません。ただし、その後に、宣言型セキュリティでは不可能な別のメソッドを呼び出した方がよい場合があります。

偽装

既定では、通常、ASP.NET アプリケーションは設計、実装、およびスケーラビリティの理由から呼び出し元を偽装しません。たとえば、偽装すると、効果的な中間層接続プールを利用できなくなるため、アプリケーションの拡張性に重大な影響を及ぼすおそれがあります。

リソース アクセス用の代替識別情報 (非プロセス識別情報) を必要とする場合などの特定のシナリオでは、偽装を必要とする可能性があります。ホスト環境では、複数の匿名識別情報は、多くの場合アプリケーション分離の一形態として使用されます。たとえば、アプリケーションがフォーム認証またはパスポート認証を使用する場合は、IIS によってアプリケーションの仮想ディレクトリに関連付けられた匿名のインターネット ユーザー アカウントを偽装できます。

呼び出し元を偽装できますが、偽装対象には、匿名のインターネット ユーザー アカウントまたは固定的な識別情報が考えられます。呼び出し元 (IIS 認証済みの識別情報) を偽装するには、次の構成を利用します。

<identity impersonate="true" />

固定的な識別情報を偽装するには、<identity> 要素に userName と password の属性をさらに追加しますが、暗号化された資格情報をレジストリに格納するために Aspnet_setreg.exe を使用していることを確認してください。構成ファイルでの資格情報の暗号化、および Aspnet_setreg.exe の詳細については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」を参照してください。

プログラムによる偽装を利用する

要求全体に対して 1 つのアカウントを偽装したくない場合は、プログラムによる偽装を利用すれば、要求の一部に対して偽装できます。たとえば、ASP.NET プロセス アカウントを使用してアプリケーションの主要なリソースやダウンストリーム データベースにアクセスしたい場合は、代替識別情報を使用して、別のリモート データベースやリモート ファイル共有のような代替リソースにアクセスする必要があります。

このためには、IIS を使用して、匿名ユーザー アカウントを信頼できる代替識別情報として構成します。次に、以下のコードで、リモート リソース アクセス コードを実行する間だけ匿名アカウントを使用して偽装トークンを作成します。

HttpContext context = HttpContext.Current;
// コンテキストからサービス プロバイダを取得します。
IServiceProvider iServiceProvider = context as IServiceProvider;
// HttpContext を表す型を取得します。
Type httpWorkerRequestType = typeof(HttpWorkerRequest);
// サービス プロバイダから HttpWorkerRequest サービスを取得します。
// 注: HttpContext から HttpWorkerRequest 型を取得しようとする場合は、
// アンマネージ コード アクセス許可が要求されます。
HttpWorkerRequest httpWorkerRequest = 
iServiceProvider.GetService(httpWorkerRequestType) as HttpWorkerRequest;
// IIS によって渡されたトークンを取得します。
IntPtr ptrUserToken = httpWorkerRequest.GetUserToken();
// トークンから WindowsIdentity を作成します。
WindowsIdentity winIdentity = new WindowsIdentity(ptrUserToken);
// ユーザーを偽装します。
Response.Write("Before impersonation: " + 
WindowsIdentity.GetCurrent().Name + "<br>");
WindowsImpersonationContext impContext = winIdentity.Impersonate();
Response.Write("Impersonating:" + WindowsIdentity.GetCurrent().Name + "<br>};
// リソース アクセス コードをここに挿入します。

// 偽装を終了します。
impContext.Undo();
Response.Write( "After Impersonating: " + 
WindowsIdentity.GetCurrent().Name + "<br>");

注: この方法は、IIS でアプリケーションの仮想ディレクトリが匿名アクセスをサポートするように構成されているフォーム認証またはパスポート認証を前提としています。

このコードを使用する場合は、次の <identity> 構成を利用します。

<identity impersonate="false" />

注: このコードは、アンマネージ コード アクセス許可 SecurityPermission(SecurityPermissionFlag.UnmanagedCode) を要求しています。また、このアクセス許可は、完全に信頼できる Web アプリケーションだけに与えられます。

機密性の高いデータ

機密性の高いデータには、アプリケーション構成の詳細 (接続文字列、サービス アカウント資格情報など) やアプリケーション固有のデータ (顧客クレジット カード番号など) があります。以下の推奨事項は、機密性の高いデータを処理するときのリスクを軽減するのに役立ちます。

  • ページどうしで機密性の高いデータを渡さないようにします。

  • 構成ファイルではプレーン テキスト パスワードを使用しません。

  • DPAPI を使用してキー管理を回避します。

  • 機密性の高いデータの出力キャッシングを無効にします。

ページどうしで機密性の高いデータを渡さない

ビュー ステート、Cookie、クエリ文字列、または隠しフォーム フィールド変数のようなクライアント側の状態管理オプションのいずれかを使用して機密性の高いデータを格納するのは避けてください。そうしなければ、データが改ざんされたり、クリア テキストで表示されるおそれがあります。したがって、サーバー側の状態管理オプション (セキュリティ保護されたデータ交換用の SQL Server データベースなど) を利用してください。

構成ファイルではプレーン テキスト パスワードを使用しない

Machine.config と Web.config における <processModel>、<sessionState>、<identity> の各要素には、userName と password の属性が用意されています。この属性はプレーン テキストで格納しないでください。暗号化された資格情報を、Aspnet_setreg.exe ツールを使用してレジストリに格納します。

構成ファイルにおいて資格情報を暗号化する方法および Aspnet_setreg.exe の詳細については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」を参照してください。

DPAPI を使用してキー管理を回避する

DPAPI は、接続文字列やサービス アカウント資格情報のようなシークレットの暗号化に最適です。ページでこのような構成データを使用する必要がある場合は、DPAPI を使用してキー管理の問題を回避します。

詳細については、モジュール 7 「セキュリティ保護されたアセンブリを構築する」の「暗号化」を参照してください。

機密性の高いデータの出力キャッシングを無効にする

ページにパスワード、クレジット カード番号、口座の状況などの機密性の高いデータが含まれている場合は、そのページをキャッシュしないでください。特定のページのキャッシュを無効にするには、次のページ レベル属性を使用します。

<%@ Page OutputCache Duration="0" Location="None" VaryByParam="None" %>

セッション管理

セキュリティ保護されたセッション管理を提供するには、2 つの主な要素を考慮する必要があります。最初に、セキュリティ保護された操作の実行対象の機密性の高いページにアクセスしたり、機密性の高いデータ項目にアクセスするためにセッション トークンを使用することができないようにします。次に、セッション データに機密性の高い項目が含まれている場合は、セッション ストアなどのセッション データをセキュリティ保護する必要があります。

次の 2 種類のトークンが、セッション管理に関連付けられています。

  • セッション トークン: このトークンは、たとえば、<sessionState> 要素のモード属性を InProc、SQLServer、または StateServer に設定してセッション状態が有効にされている場合は、ASP.NET で自動的に生成されます。

    注: <sessionState> 構成を無効にし、@Page タグの EnableSessionState 属性を使用してページ単位にセッションの状態を無効または有効にすることができます。

  • 認証トークン: このトークンは、認証済みのユーザーのセッションを追跡するために、フォーム認証などの認証メカニズムで生成されます。有効な認証トークンにより、ユーザーが Web サイトの制限された部分にアクセスできます。

以下の推奨事項は、セキュリティ保護されたセッション管理を構築するのに役立ちます。

  • 機密性の高いページでは認証を必要とします。

  • クライアント側の状態管理オプションを利用しません。

  • セッション トークンと認証トークンを混同しません。

  • SSL を有効に利用します。

  • セッション データをセキュリティ保護します。

機密性の高いページの認証を必要とする

ユーザーがサイトの機密性の高い部分や制限された部分にアクセスする前にユーザーを認証していることを確認してください。セキュリティ保護された認証を利用して SSL で認証トークンを保護すると、攻撃者がセッション トークンを強奪したり再利用することができないため、ユーザーのセッションはセキュリティ保護されます。攻撃者が承認ゲートを通過するためには、認証トークンが必要です。

フォーム認証に備えて認証トークンをセキュリティ保護する方法の詳細については、このモジュールで前述の「フォーム認証」を参照してください。

クライアント側の状態管理オプションを利用しない

ビュー ステート、Cookie、クエリ文字列、または隠しフォーム フィールドのようなクライアント側の状態管理オプションのいずれかを使用して機密性の高いデータを格納するのは避けてください。そうしなければ、情報が改ざんされたり、クリア テキストで表示されるおそれがあります。サーバー側の状態管理オプション (データベースなど) を使用して機密性の高いデータを格納します。

セッション トークンと認証トークンを混同しない

セキュリティ保護されたセッション管理では、2 種類のトークンを混同しないようにしてください。最初に、認証トークンをセキュリティ保護し、攻撃者がアプリケーションの制限区域にアクセスするために認証トークンを捕捉して使用することができないようにします。次に、セッション トークンを単独で使用しても機密性の高いページまたはデータにはアクセスできないような方法でアプリケーションを作成します。セッション トークンは、情報の個別化のため、または複数の HTTP 要求にわたるユーザー状態を保持するためだけに使用する必要があります。認証を行わない場合は、ユーザー状態の機密性の高い項目は保持しないでください。

SSL を有効に利用する

サイトにセキュリティ保護された区域や公開アクセス区域がある場合は、セキュリティ保護された認証済み区域を SSL で保護する必要があります。ユーザーがセキュリティ保護された区域と公開区域を行き来する場合、ASP.NET で生成されたセッション Cookie (または Cookie なしセッション状態を有効にした場合は URL) がプレーン テキストでやりとりされますが、Secure Cookie プロパティが設定されている限り、暗号化されていない HTTP 接続では、認証 Cookie は渡されません。

注: フォーム認証 Cookie に Secure プロパティを設定するには、<forms> 要素に requireSSL="true" を設定します。

攻撃者は、暗号化されていない HTTP セッションで渡されたセッション Cookie を取得できます。ただし、サイトを正しく設計しており、制限されたページやリソースを個々のセキュリティ保護されたディレクトリに配置している場合、攻撃者は、セッション Cookie を使用してもセキュリティ保護されていない公開アクセス ページにしかアクセスできません。この場合、こうしたページでは機密性の高い操作を実行しないため、セキュリティの脅威はありません。攻撃者が、保護されたページに対するセッション トークンを再利用しようとしても、認証トークンがないため、攻撃者はアプリケーションのログイン ページにリダイレクトされます。

Secure Cookie プロパティの使用方法およびセキュリティ保護されたフォーム認証ソリューションの構築方法の詳細については、このモジュールで前述の「フォーム認証」を参照してください。

セッション データをセキュリティ保護する

サーバー上のセッション データに機密性の高い項目が含まれている場合は、そのデータとストアをセキュリティ保護する必要があります。ASP.NET では、いくつかのセッション状態モードをサポートしています。ASP.NET セッション状態をセキュリティ保護する方法の詳細については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」の「セッション状態」を参照してください。

パラメータ改ざん

パラメータ (フォーム フィールド、クエリ文字列、ビュー ステート、Cookie などで使用されているパラメータ) は、いつも制限されたページにアクセスしようとしたり、許可されていない操作を実行するようにアプリケーションに仕掛けを施す攻撃者によって改ざんされる場合があります。

たとえば、Cookie 内で推測可能な数字のような脆弱な認証トークン方式を使用していることが攻撃者に知られている場合、攻撃者は、別の数字を持つ Cookie を作成して、異なる (おそらく特権のある) ユーザーとして要求する可能性があります。

以下の推奨事項は、パラメータ改ざんの脆弱性を回避するのに役立ちます。

  • MAC でビュー ステートを保護します。

  • Page.ViewStateUserKey を使用してワンクリック攻撃に対抗します。

  • 機密性の高いデータはサーバーに保持します。

  • 入力パラメータを検証します。

MAC でビュー ステートを保護する

Web ページまたはコントロールで、ビュー ステートを使用して複数の HTTP 要求にわたる状態を保持する場合は、ビュー ステートが暗号化されており、MAC を利用して整合性の検査が行われていることを確認します。既定では、Machine.config における <pages> 要素の enableViewStateMac 属性により、確実に MAC を利用してビュー ステートが保護されます。

<pages buffer="true" enableSessionState="true"
enableViewState="true" enableViewStateMac="true" 
autoEventWireup="true" validateRequest="true"/>

注: @Page ディレクティブでも前述の属性をサポートしているため、設定をページ単位でカスタマイズできます。

ビュー ステートをコントロール単位、ページ単位、またはアプリケーション単位で有効にするかどうかは無視しても問題ありませんが、ビュー ステートを使用するときは必ず enableViewStateMac が True に設定されていることを確認してください。

Server.Transfer

以下に示すようにアプリケーションで Server.Transfer を使用して、省略可能な 2 番目のブール値のパラメータを、QueryString コレクションと Form コレクションが維持されるように True に設定した場合、enableViewStateMac を True に設定すると、このコマンドは失敗します。

Server.Transfer("page2.aspx", true);

2 番目のパラメータを省略するか、False に設定した場合、エラーは発生しません。enableViewStateMac を False に設定せずに、QueryString コレクションと Form コレクションを保持したい場合は、Microsoft サポート技術情報 316920「[PRB] Server.Transfer を使用するとエラー メッセージ "ViewState は無効です" が表示される」で説明している回避策に従ってください。

ビュー ステートの暗号化および整合性の検査に備えて <machineKey> 要素を構成する方法については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」を参照してください。

Page.ViewStateUserKey を使用してワンクリック攻撃に対抗する

呼び出し側を認証し、ビュー ステートを使用する場合は、ワンクリック攻撃を防ぐために Page_Init イベント ハンドラで Page.ViewStateUserKey プロパティを設定します。攻撃者が、ビュー ステートを備えた入力済みの Web ページ (.htm または .aspx) を作成した場合に、ワンクリック攻撃が発生します。ビュー ステートは、攻撃者が以前に作成したページ (100 項目を提示したショッピング カート ページなど) から生成される可能性があります。攻撃者は、疑いを持たないユーザーにこのページを参照させ、そのページをビュー ステートが有効になっているサーバーに送信させます。サーバーには、ビュー ステートが攻撃者から発生したことを知る方法がありません。ビュー ステート検証および MAC でこの攻撃に対応していない理由は、ビュー ステートが有効で、ページがユーザーのセキュリティ コンテキストの下で実行されるためです。

ワンクリック攻撃への対策として Page.ViewStateUserKey プロパティを適当に固有の値に設定します。この値は、各ユーザーに固有でなければならず、一般にユーザー名または識別子です。攻撃者がビュー ステートを作成すると、ViewStateUserKey プロパティは攻撃者の名前に初期化されます。ユーザーがサーバーにページを送信すると、そのページは攻撃者の名前で初期化されます。その結果、ビュー ステートの MAC 確認は失敗し、例外条件が生成されます。

注: 通常、この攻撃が、匿名で参照する (ユーザー名が利用できない) ページで問題にならない理由は、この種のページで機密性の高いトランザクションを作成してはならないためです。

機密性の高いデータはサーバーに保持する

入力パラメータは信用しないでください。特に、そうしたパラメータを使用してサーバーのセキュリティ意思決定を行う場合に注意が必要です。また、機密性の高いデータにクリア テキスト パラメータを使用しないでください。代わりに、サーバー上の機密性の高いデータをセッション ストアに格納し、セッション トークンを使用してストア内の項目を参照します。ユーザーが安全に認証されていること、および認証トークンが正しくセキュリティ保護されていることを確認してください。詳細については、このモジュールの前半の「セッション管理」を参照してください。

入力パラメータを検証する

フォーム フィールド、クエリ文字列、Cookie、および HTTP ヘッダーからのすべての入力パラメータを検証します。System.Text.RegularExpressions.Regex クラスは、入力パラメータの検証に役立ちます。たとえば、次のコードは、このクラスを使用してクエリ文字列パラメータを介して渡される名前を検証する方法を示しています。同じ技法を利用して、Cookie、フォーム フィールドなどからの別形式の入力パラメータも検証できます。たとえば、Cookie パラメータを検証するには、Request.QueryString の代わりに Request.Cookies を使用します。

using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// 名前には、1 ~ 40 桁の英数字、および
// 必要に応じて D'Angelo のような名前に対する
// 特殊文字 '`´ を指定します。
if (!Regex.IsMatch(Request.QueryString["name"], 
@"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
throw new Exception("Invalid name parameter");
// 個別に正規表現を利用して他のすべての
// クエリ文字列パラメータを検証する
  . . .
}

正規表現の利用方法および入力データの検証方法の詳細については、このモジュールで前述の「入力検証」を参照してください。

例外管理

Web ページでの例外を正しく処理すれば、機密性の高い例外詳細がユーザーに露呈されるのを避けることができます。以下の推奨事項は、ASP.NET の Web ページおよびコントロールに適用されます。

  • クライアントに一般的なエラー ページを返します。

  • ページ レベルまたはアプリケーション レベルのエラー ハンドラを実装します。

例外管理の詳細については、モジュール 7 「セキュリティ保護されたアセンブリを構築する」を参照してください。

クライアントに一般的なエラー ページを返す

未処理の例外 (つまり、アプリケーション境界に及ぶ例外) の場合には、ユーザーに一般的なエラー ページを返します。このために、<customErrors> 要素を次のように構成します。

<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />

エラー ページには、適度に一般的なエラー メッセージ、およびできればサポートの詳細を用意する必要があります。エラーを生成したページの名前は、aspxerrorpath クエリ パラメータを介してエラー ページに渡されます。

また、各種エラーに対して複数のエラー ページを使用することもできます。次に例をあげます。

<customErrors mode="On" defaultRedirect="YourErrorPage.htm">
<error statusCode="404" redirect="YourNotFoundPage.htm" />              
<error statusCode="500" redirect="YourInternalErrorPage.htm" />              
</customErrors>

個々のページについては、次のページ レベル属性を使用してエラー ページを提供できます。

<% @ Page ErrorPage="YourErrorPage" %>

ページ レベルまたはアプリケーション レベルのエラー ハンドラを実装する

未処理の例外をページ レベルでトラップして処理する必要がある場合は、以下と同様の Page_Error イベントのハンドラを作成します。

public void Page_Error(object sender,EventArgs e)
{
// ソース例外の詳細を取得します。
Exception ex = Server.GetLastError();
// 詳細を診断用のイベント ログに書き込みます。
  . . .
// 例外によるアプリケーション レベル イベント (Application.Error) の
// 通知および生成を防止します。
Server.ClearError();
}  

例外をページ ハンドラから通知できる場合や、ページ ハンドラがない場合は、アプリケーション エラー イベントが発生します。アプリケーション レベル イベントをトラップするには、次のように、Global.asax に Application_Error を実装します。

protected void Application_Error(Object sender, EventArgs e) 
{
//  イベント ログに書き込みます。
}

監査とログ記録

Web アプリケーションの既定の ASP.NET プロセスでイベント ログに新しいレコードを書き込むことができますが、このプロセスには、新しいイベント ソースを作成するのに十分なアクセス許可が設定されていません。この問題に対処するには、次の 2 つの方法があります。インストーラ クラスを作成できます。このクラスは、管理者特権を利用できる場合はインストール時に呼び出されます。あるいは、EventLog レジストリ キーのアクセス許可を構成して、ASP.NET プロセスで、または識別情報を偽装して実行時にイベント ソースを作成できます。前者の方法をお勧めします。

  • インストール時にアプリケーション イベント ソースを作成するには

  1. Visual Studio .NET のソリューション エクスプローラ ウィンドウでプロジェクトを右クリックして、[追加] をポイントし、[コンポーネントの追加] をクリックします。

  2. テンプレートのリストから [インストーラ クラス] を選択し、適切なクラス ファイル名を指定します。

    こうして、RunInstaller(true) 属性による注釈付きの新しいインストーラ クラスが作成されます。

    RunInstaller(true)
    public class EventSourceInstaller :System.Configuration.Install.Installer
    {
     . . .
    }
    
  3. デザイン ビューで新しいインストーラ クラスを表示し、ツールボックスを表示した後、ツールボックスで [コンポーネント] をクリックします。EventLogInstaller コンポーネントをデザイナの作業領域にドラッグします。

    注: EventLogInstaller がツールボックスに表示されない場合は、ツールボックスを右クリックして、[アイテムの追加と削除] をクリックします。次に、EventLogInstaller を選択して、このコンポーネントの種類を追加します。

  4. 以下の EventLogInstaller プロパティを設定します。

    • Log: このプロパティを "Application" に設定し、イベント ログ名として使用する必要があります。既定の Application ログを使用するか、アプリケーション固有のログを作成できます。

    • Source: このプロパティをイベント ソース名に設定します。通常は、これがアプリケーション名になります。

  5. プロジェクトを構築し、インストール時にインストーラ クラスのインスタンスを作成します。

    .NET セットアップおよび展開プロジェクトを使用して Windows インストーラ ファイル (.msi) を作成する場合は、インストーラ クラス インスタンスが自動的に作成され、呼び出されます。xcopy または同等の展開を利用する場合は、InstallUtil.exe ユーティリティを使用してインストーラ クラスのインスタンスを作成し、それを実行します。

  6. イベント ソースが正しく生成されたことを確認するには、レジストリ エディタを使用して次のキーに移動します。

    HKLM\System\CurrentControlSet\Services\EventLog\Application\{source name}
    

    このキーが存在すること、および既定の .NET Framework イベント メッセージ ファイルを示す次のような EventMessageFile 文字列値が含まれていることを確認します。

    \Windows\Microsoft.NET\Framework\{version}\EventLogMessages.dll

既存のアプリケーションがあるのでインストーラ クラスを作成したくない場合は、ASP.NET プロセスにイベント ログ レジストリ キーに対する正しいアクセス権を与える必要があります。レジストリ キーの詳細および必要な正しいアクセス権については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」の「イベント ログ」を参照してください。

EventLogPermission

イベント ログに書き込むコードには、コード アクセス セキュリティ ポリシーによって EventLogPermission を与える必要があります。ただし、このようにすると、Web アプリケーションが部分的に信用できるレベルで実行するように構成されている場合は問題になります。部分的に信用できる Web アプリケーションからイベント ログに書き込む方法については、モジュール 9 「ASP.NET でコード アクセス セキュリティを使用する」を参照してください。

要約

このモジュールは、Web ページやコントロールを構築するときに対処する必要のある主な脅威を示すことから始めました。多くのアプリケーション レベルの攻撃は、入力検証の脆弱性を利用します。この脆弱性に特別に注意を払い、検証方針が信頼できることや、信頼されていないソースで処理されるすべてのデータが正しく検証されていることを確認してください。もう一つの一般的な脆弱性は、認証 Cookie の保護を怠ることです。このモジュールの「フォーム認証」では、許可されていないアクセス、セッション ハイジャック、および Cookie リプレイ攻撃を防ぐために適用する効果的な対策を示しました。

その他のリソース

詳細については、以下のリソースを参照してください。

  • セキュリティ保護された Machine.config および Web.config の構成を確立する方法の詳細については、モジュール 19 「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」を参照してください。

  • 印刷可能なチェックリストについては、このガイドの「チェックリスト: ASP.NET をセキュリティ保護する」を参照してください。

  • 開発者ワークステーションをセキュリティ保護する方法については、このガイドの「How To」の「[HOWTO] 開発者のワークステーションのセキュリティを確保する方法」を参照してください。

  • ASP.NET での認証および承認の詳細については、Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「第 8 章 ASP.NET セキュリティ」(http://www.microsoft.com/japan/msdn/net/security/SecNetch08.aspx) を参照してください。

  • フォーム認証の利用方法のウォーク スルーについては、Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」の「SQL Server 2000 でフォーム認証を使用する方法」および「Active Directory でフォーム認証を使用する方法」(http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx) を参照してください。

  • 正規表現の利用方法の詳細については、Microsoft サポート技術情報 308252「[HOW TO] 正規表現および Visual C# .NET を使用して、パターンマッチングを行う方法」を参照してください。

  • ASP.NET でのユーザー入力検証の詳細については、MSDN の記事「User Input Validation in ASP.NET」(英語) (http://msdn.microsoft.com/library/en-us/dnaspp/html/pdc_userinput.asp) を参照してください。

  • Secure Cookie プロパティの詳細については、W3C Web サイトの「RFC2109」(英語) (http://www.w3.org/Protocols/rfc2109/rfc2109) を参照してください。

  • Open Hack コンテストでのセキュリティ考慮事項の詳細については、MSDN の記事「Building and Configuring More Secure Web Sites」(英語) (http://msdn2.microsoft.com/en-us/library/Aa302370.aspx) を参照してください。


Page view tracker