IIS 7.0 (Windows Server 2008) 以降のアーキテクチャについて
こんにちは。
日本マイクロソフト、CSS デベロッパー ツールズ DSI の牧です。
Windows 2000 時代の IIS 5.0 は inetinfo.exe というプロセスが一つでほぼすべてを取り仕切っていました。Windows Server 2003 の IIS 6.0 では HTTP 通信部分が http.sys、アプリケーションワーカープロセスは w3wp.exe と役割分担がされました。これによってパフォーマンスやセキュリティが向上しましたが、他方、問題が発生した際にいったいどの部分でエラーとなっているかが、分かりにくくなっている事は否めません。
本トピックでは、IIS 7.0(Windows Server 2008)以降、IIS を構成するコンポーネントを説明し、それらコンポーネントがどの様に処理を行うかを解説したうえで、本題の IIS が行うエラー制御処理についてどこでエラーが起きたらどのように観測されるのか、といった点を簡単に解説します。
IIS を構成するコンポーネントについて
まず、IIS のコアコンポーネントをご紹介したいと思います。
- HTTP プロトコルスタック (HTTP.sys)
HTTP.sys は、端的に言うと HTTP/HTTPS 用の通信ドライバです。HTTP コネクションの管理、HTTP ヘッダー解析、URL/URI/IRI の解析、HTTP リクエストのログ機能、キャッシュ機能、および、SSL 機能等、多くの機能が HTTP.sys に搭載されています。 - Windows プロセスアクティブ化サービス (Windows Process Activation Service : WAS)
WAS は、ワーカープロセスを管理するサービスです。実行中のワーカープロセスの健常性を監視しています。また、WASは、構成情報の読み取りなども行います。 - World Wide Web 発行サービス (World Wide Web Publishing Service : W3SVC)
W3SVCは、HTTP.sys 及び WAS と通信を行い、HTTP.sys に構成情報を提供します。また、W3SVC は、Web サイトのパフォーマンス監視も行います。 - 構成ストア
構成ストアは、XML 形式の設定ファイル群を表す仮想的なストアです。IIS 全体の構成情報は、 IIS のサーバーレベルの構成ファイルである、applicationHost.config に格納されています。この中には IIS の構成が含まれます。また、アプリケーションの構成は、ご存じの通り、各アプリケーションの web.config に含まれています。 - ワーカープロセス
ワーカープロセスは、要求の処理と応答の生成を行うプロセスです。ワーカープロセス には、”.NET 統合モード” と “クラシックモード” の 2 種類の動作モードがありますが、本稿では .NET 統合モードについてのみ解説します。
IIS がリクエストを処理する流れについて
それでは、クライアントからの要求がどのようにコンポーネントを通って処理され、応答が返るのかを簡単に説明します。
- HTTP要求がクライアントブラウザからサーバーへ到着します。HTTP.sys が要求を受け取ります。
- HTTP.sys が要求の送信先アプリケーションの構成情報が登録されているか確認をし、登録されていなかった場合は、HTTP.sys は W3SVC に問い合わせを行います。
- W3SVC は、WAS へ要求を渡します。
- WASは、IIS のサーバーレベルの構成情報である、applicationHost.config を構成ストアから取得します。
- WASは、要求されたアプリケーションプール内のワーカープロセスを確認します。ワーカープロセスが存在しない場合は、そのアプリケーションプール用のワーカープロセスを起動します。
- WASより、アプリケーションプールの情報や構成情報を W3SVC に渡します。
- W3SVC は、WASから受け取った構成情報に基づき、HTTP.sys へ設定および更新の通知をします。
- HTTP.sysは、その通知を基に、アプリケーション用のキューを作成してから、適切なワーカープロセスに要求を転送します。
- 要求を受け取ったワーカープロセスは、要求処理を開始します。
- 要求処理パイプラインのそれぞれの処理を行い、ワーカープロセスは、要求の処理結果を HTTP.sys へ返します。
- HTTP.sys がクライアントに応答を送信します。
IIS が不正なリクエストを処理する際に起こるエラーについて
このように多くのコンポーネントがリクエスト処理に関わるため、リクエストに問題がある場合、その各所でエラーが生じる可能性があります。
特に注意する必要がある箇所としては、各コンポーネントが行うエラー制御処理は独立しており、相互に干渉することが出来ないという点にあります。結果、例えば http.sys がクライアントへ応答するエラーはカスタマイズできないといった動作上の制限が生じます。
以下に、不正なリクエストがどのコンポーネントで弾かれる可能性があるか、またその場合、エラーがどのように見えるかを記載しました。
http.sys
RFC に反するような HTTP リクエストの多くは、http.sys で弾かれます。
例えば、ヘッダーに不正な文字列が設定されていたり、ヘッダーとして不正な場合、RFC 3987(私用領域の文字を使用している)等に違反するリクエストの場合などには、HTTP の要求を最初に受け付ける http.sys によって 400 Bad Request が返されます。この事は http.sys のエラーログにも記録されます。
HTTP API でのエラー ログ
https://support.microsoft.com/kb/820729/ja
なお、この場合、http.sys でリクエストを弾いているため、該当の要求は IIS や IIS 上で動作するアプリケーションまで到達しません。また、http.sys 側で HTTP エラー 400 に対して任意のエラー ページを返すような設定は用意されていないため、http.sys が返すエラー (HTTP エラー 400 / 503 等) が発生した際に、カスタムのエラーページを表示する方法はありません。
Configuring HTTP Error Responses in IIS 7
https://technet.microsoft.com/en-us/library/cc731570(v=WS.10).aspx
w3wp.exe(IIS 部分)
IIS には、以前 URLScan というアドオンで提供されていた要求をフィルタリングする機能が、requestFiltering として製品に組み込まれました。
要求のフィルタリング <requestFiltering>
https://technet.microsoft.com/ja-jp/library/ee431637.aspx
例えば、requestLimits 要素には、要求データ長を制限する maxAllowedContentLength 属性、要求クエリ長を制限する maxQueryString 属性、要求 URL 長を制限する maxUr属性があり、denyUrlSequences 要素では URL 中の特定文字列を拒否できます。
要求制限 <requestLimits>
https://technet.microsoft.com/ja-jp/library/ee431638.aspx
URL シーケンスの拒否 <denyUrlSequences>
https://technet.microsoft.com/ja-jp/library/ee431583.aspx
これら IIS の要求制限については httpErros 要素によってエラーページが表示されます。なお、existingResponse 属性によって後述の ASP.NET によるエラーの動作に影響が生じる場合があります。
HTTP エラー <httpErrors>
https://technet.microsoft.com/ja-jp/library/ee431601.aspx
w3wp.exe(ASP.NET 部分)
ASP.NET には要求検証機能があります。特に代表的なものとして、スクリプトのインジェクションとみなされるリクエストにはエラーが表示されます。
How To: ASP.NET でインジェクション攻撃から保護する方法
https://msdn.microsoft.com/ja-jp/library/ff647397.aspx
スクリプトによる攻略の概要
https://msdn.microsoft.com/ja-jp/library/w1sw53ds(v=vs.110).aspx
ASP.NET の検証機能は httpRuntime 要素にて設定可能です。例えば、要求データ長を制限する maxRequestLength 属性、要求 URL 長を制限する maxUrlLength 属性、要求ファイル名検証に関わる relaxedUrlToFileSystemMapping 属性、さらに前述の XSS 検証に関わる requestValidationMode 属性、requestValidationType 属性、requestPathInvalidCharacters 属性などがあります。
httpRuntime 要素 (ASP.NET 設定スキーマ)
https://msdn.microsoft.com/ja-jp/library/e1f13641(v=vs.110).aspx
ASP.NET のこれらの検証機能によってエラーとなった場合、基本的にはカスタムエラー設定に従った動作をします(問題によっては強制的に接続を切断する場合もあります)。しかし、customErrors 要素の redirectMode 属性が redirect であり、かつリダイレクト先のエラーページを表示する事にも支障がある場合、ランタイムエラーとなる場合があります。
customErrors 要素 (ASP.NET 設定スキーマ)
https://msdn.microsoft.com/ja-jp/library/vstudio/h0hfz6fc(v=vs.100).aspx
また、ASP.NET で構築されている部分、例えばASP.NET のモジュール(ビルトインの ASP.NET FileAuthorizationModule や URL 解釈機能を含みます)やハンドラで、リクエスト処理に支障があった場合も ASP.NET のカスタムエラーが動作します。エラーコードは発生する問題によって異なる場合があります。
FileAuthorizationModule クラス
https://msdn.microsoft.com/ja-jp/library/System.Web.Security.FileAuthorizationModule(v=vs.110).aspx
エラーの追及方法について
上記のようにやや複雑の構成となっていますが、該当のリクエストがどのコンポーネントで拒否されたかを知る事で、適切な対処が選択できます。
そのことを知るためには、IIS 7.0 以降に搭載された「失敗した要求トレース」が非常に有用です。以下にその設定方法と使い方に関するドキュメントがあります。
失敗した要求トレース <traceFailedRequests>
https://technet.microsoft.com/ja-jp/library/ee431657.aspx
失敗した要求トレースを使用して Classic ASP エラーをトラブルシューティングする
https://technet.microsoft.com/ja-jp/library/ee155452.aspx
エラーはどこで起きたのか、という点に注目してトレースを見て頂くと、それに応じて、どのコンポーネントの検証機能によってエラーとなったのか、が分かるかと思います。