「ASP.NETでクエリを検証する機能は、基本的な入力制御を実行するように設計されています。 開発中のWebアプリケーションのセキュリティに関する決定を行うことを意図したものではありません。 開発者のみが、コードで処理するコンテンツを決定できます。 マイクロソフトは、任意のソースから受信したすべての入力を確認することをお勧めします。 お客様による安全なアプリケーションの開発を促進するよう努めており、リクエスト検証機能は、この方向で開発者を支援するために設計および実装されています。 開発に関する推奨事項の詳細については、MSDNの記事msdn.microsoft.com/en-us/library/ff649487.aspx#pagguidelines0001_inputdatavalidationを参照してください 。
マイクロソフトセキュリティレスポンスセンターASP.NETリクエスト検証公式ステータス
ASP.NETでクエリ検証をバイパスするさらに別の方法の発見に関するQuotium Research Centerの最近のレポートに対するMSRCからのこのような突然の応答にもかかわらず、Webアプリケーションのセキュリティに関する決定を下すことを意図していることは注目に値します。 これは、チェックの主要なセット(System.Web.CrossSiteScriptingValidation)を実装するクラスの名前と、XSS Type-1攻撃の特定のサブセット(クロスサイトスクリプティングの実行の反映)の防止にあるその本質によってサポートされ、Webスタックの開発者からの元の記事 。 別の質問は、この機能をどのように効果的に実装でき、XSS Type-1ベクトルから保護する既存の基本的な通常のフィルターから本格的なWebアプリケーションファイアウォールを取得する方法ですか?
この質問に答えるには、.NET Frameworkのさまざまなバージョンでのクエリ検証の実装の詳細、その制限、よく知られている回避策、および機能を拡張する可能性を理解する必要があります。
1. ASP.NETリクエスト検証の進化
ASP.NETのすべてのバージョン(.NET Frameworkのバージョンと一致)で、v1.1からv4.5で終わるクエリ検証は、危険な値のブラックリストを記述するレギュラーセットのチェーンのオカレンスのHTTPリクエストのさまざまな要素の検索に限定されます。 コーディングの観点からは、標準の正規表現を使用せずに、パフォーマンス上の理由で手動で実装される認識オートマトンによって実装されます。 多くの危険な値には、十分な予備処理を行わずに出力ドキュメントで使用された場合、出力ドキュメントの整合性に違反する可能性があるHTML言語の要素が含まれています。
初めて、クエリ検証メカニズムがASP.NET v1.1に実装され、かなり広いブラックリストが使用されました。 クエリ文字列またはフォームフィールド値のパラメーターが正規表現のいずれかに対応する場合、要求処理はブロックされました。
- <?i [az!/]
- (?i:スクリプト)\ s?\:
- (?i:on [az])* \ s * =
- (?i:ex)pression \(
多数の誤検知のため、開発者がこの機能を完全に無効にすることを好むのは驚くことではありません。 したがって、ASP.NET v2.0では、多くの危険な値が大幅に削減され、変更されずにv4.5に達しました。
- #&
- <?i [az!/ \?]
ASP.NET v4.0の準備段階で、開発者はセット(?I:スクリプト)\ s?:がリストに返されることも発表しましたが、これはv4.0またはv4.5では発生しませんでした。
多くの危険な値がバージョンごとに変更されただけでなく、検証の範囲と開発者がこのプロセスを制御する可能性も変更されました。 そのため、v2.0では個々のページのリクエスト検証を無効にすることが可能になり、v4.0ではいわゆる新しいモードが導入されました。 遅延個別検証。要求の予備処理の段階ではなく、Webアプリケーションコードからアクセスされたときに各パラメーターがチェックされます。 v4.0以降、クエリ文字列パラメーターとフォームフィールド値に加えて、検証領域には以下も含まれます。
- Request.Cookiesのすべての要素の値。
- Request.Filesからダウンロードしたファイルの名前。
- Request.RawUrl、Request.PathおよびRequest.PathInfoの値
2. ASP.NET v4.xでのリクエストの検証
ASP.NETの最近のバージョンでは、要求の検証の一部として、ライフサイクルの最も早い段階で実行されるいくつかの追加チェックも実行されます。 それらの完全なリストを表に示します。
確認 | 設定と値 |
---|---|
| <httpRuntime>セクションのmaxUrlLength属性。 アプリケーション全体と個々の仮想パスまたはページの両方に対してグローバルに定義できます。
260文字を超えるパスを含むHTTP要求の処理をブロックします。 この値は、 IISまたはhttp.sysで定義された制限まで増やすことができます。 |
クエリ文字列を含むRequest.RawUrlフラグメントの長さを確認する | <httpRuntime>セクションのmaxQueryStringLength属性。 アプリケーション全体と個々の仮想パスまたはページの両方に対してグローバルに定義できます。
2048文字を超えるクエリ文字列を含むHTTP要求の処理をブロックします。 この値は、 IISまたはhttp.sysの制限まで増やすことができます。 |
ASP.NETで潜在的に危険な文字のRequest.Pathをスキャンします | <httpRuntime>セクションのrequestPathInvalidCharacters属性。 アプリケーション全体と個々の仮想パスまたはページの両方に対してグローバルに定義できます。
パスに次の文字が含まれている場合、HTTPリクエストの処理をブロックします。
requestPathInvalidCharacters属性では、不正な文字は二重引用符で囲まれ、コンマで区切られます。 パス "\ .."の文字シーケンスは、IIS v6 +がURIの正規化を自動的に実行し、そのようなシーケンスを正しく処理するため、このリストには含まれません。 実際には、パス内のスラッシュの出現に関連するエラーも発生しません。 正規化の過程で、それらは逆に置き換えられます。 |
各Request.Path値の適切な管理された構成を見つける | <httpRuntime>セクションのrelaxedUrlToFileSystemMapping属性。 アプリケーション全体に対してグローバルにのみ決定できます。
既定では、この属性はfalseに設定されています。これにより、ASP.NETは、URLのコンポーネントパスをNTFSルールに準拠する有効なファイルパスと見なす必要があります。 この制限を無効にするには、属性値をtrueに設定します。 |
潜在的に危険な値について、Request.QueryString、Request.Form、Request.Files、Request.Cookies、Request.Path、Request.PathInfo、Request.RawUrlを確認する | <httpRuntime>セクションのrequestValidationMode属性。 アプリケーション全体と、個々の仮想パスまたはページの両方に対してグローバルに定義できます。
Webアプリケーションへの要求を検証するモードを設定します。 値4.0(デフォルト)には、Webアプリケーションコードが検証領域の要素に直接アクセスするときに発生する、遅延詳細検証が含まれます。 この属性を2.0に設定すると、以前のバージョンのASP.NETで使用されていた検証モードが返されます。 <httpRuntime>セクションのrequestValidationType属性。 アプリケーション全体に対してグローバルにのみ決定できます。 クエリ検証機能を実装するRequestValidatorクラスの継承のタイプを設定します。 デフォルトでは、System.Web.Util.RequestValidatorクラスが使用されます。 |
3.リクエスト検証の内部構造
System.Web.Util.RequestValidatorクラスのIsValidRequestStringメソッドのソースコードは、ASP.NET v2.0 +でリクエストを検証するためにデフォルトで使用され、次のようになります。
protected internal virtual bool IsValidRequestString( HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) { if (requestValidationSource == RequestValidationSource.Headers) { validationFailureIndex = 0; return true; } return !CrossSiteScriptingValidation.IsDangerousString(value, out validationFailureIndex); }
IsValidRequestStringメソッドを呼び出す前でも、ゼロバイトのすべての出現は、valueパラメーターで渡された文字列から切り取られることに注意してください。 この動作は、HttpRequestクラスのValidateStringメソッドに実装されており、開発者がオーバーライドすることはできません。
ソースコードからわかるように、クエリ検証の主な機能は、CrossSiteScriptingValidationクラスのIsDangerousStringメソッドに実装されています。
internal static bool IsDangerousString(string s, out int matchIndex) { matchIndex = 0; int startIndex = 0; while (true) { int num2 = s.IndexOfAny(startingChars, startIndex); if (num2 < 0) { return false; } if (num2 == (s.Length - 1)) { return false; } matchIndex = num2; char ch = s[num2]; if (ch != '&') { if ((ch == '<') && ((IsAtoZ(s[num2 + 1]) || (s[num2 + 1] == '!')) || ((s[num2 + 1] == '/') || (s[num2 + 1] == '?')))) { return true; } } else if (s[num2 + 1] == '#') { return true; } startIndex = num2 + 1; } }
明らかに、このフィルターは、通常のセット<?I [az!/ \?]のチェーンの発生を認識するオートマトンです|&#渡されたストリング内。 さらに、CrossSiteScriptingValidationクラスは、機能の拡張または変更には使用できない2つの補助メソッドも定義します。
internal static bool IsDangerousUrl(string s) { if (string.IsNullOrEmpty(s)) { return false; } s = s.Trim(); int length = s.Length; if (((((length > 4) && ((s[0] == 'h') || (s[0] == 'H'))) && ((s[1] == 't') || (s[1] == 'T'))) && (((s[2] == 't') || (s[2] == 'T')) && ((s[3] == 'p') || (s[3] == 'P')))) && ((s[4] == ':') || (((length > 5) && ((s[4] == 's') || (s[4] == 'S'))) && (s[5] == ':')))) { return false; } if (s.IndexOf(':') == -1) { return false; } return true; } internal static bool IsValidJavascriptId(string id) { if (!string.IsNullOrEmpty(id)) { return CodeGenerator.IsValidLanguageIndependentIdentifier(id); } return true; }
最初のURLはURLをチェックし、^(?I:https?:) | [^:]セットを満たさないすべての危険な値を考慮します。 2番目は、言語識別子の文法規則に対して引数の値をチェックします:^(?I:[a-z _] [a-z0-9 _])$。 両方のメソッドは、ASP.NET v1.1のクエリ検証の一部としてIsDangerousStringから呼び出されました。 他のすべてのバージョンでは、一部のASP.NET WebFormsコントロールでのみ機能検証メソッドとして使用され、RequestValidationExceptionは発生しません。
4.標準実装の欠点とそれを回避する方法
明らかに、クエリ検証の標準実装にはいくつかの欠点があり、Webアプリケーションのセキュリティに関する決定を下すには本当に不向きです。
第一に、調査されたチェックは、実行するためにタグを開く必要があるXSSタイプ1攻撃の限られたサブセットからのみ保護できます。 タグ、属性、またはクライアントスクリプトのコード内にパラメーター値を埋め込んだ結果、反射されたXSSの攻撃が可能な場合、標準のクエリ検証ではそれを防ぐことができません。
第二に、ブラックリスト制御自体は、セキュリティの十分な尺度ではありません。 これは、標準のクエリ検証をバイパスするいくつかのよく知られた方法の存在によるものです。
- <?I [az!/ \?]の制限は、開き山かっことタグ名の間にパーセント記号を使用することで回避できます(<%img src =#onerror = alert(1)/>)。 この場合、HTMLパーサーIE v9-はこれを有効なタグ定義と見なします。 場合によっては、WebアプリケーションがクエリパラメータのUnicode正規化を実装している場合、Unicode全体の値(%uff1c img%20src%3D%23%20onerror%3Dalert%281%29%2f%uff1e)の使用をバイパスすることもできます。
- (?I:script)\ s?\:および(?I:ex)pression \(スクリプト内および式と開始ブラケットの間の空白を使用して回避(java%09script:alert(1)and expression%09(アラート(1)))。
- #&の制限は、HTMLエンティティへの名前付き参照の存在を考慮しません。これは多くのベクターで使用することもできます(javascript%26Tab ;: alert(1))。 また、ASP.NET HTMLデコーダー(HttpUtility.HtmlDecode)の標準実装は、HTMLエンティティへの253の名前付き参照の存在のみを「認識」しますが、HTML標準ではそれらのかなり多くを定義します 。 これにより、Webアプリケーションが入力データの予備処理の段階でパラメーター値のHTMLデコードを実行する場合でも、多くのHTMLエンティティを出力ドキュメントにスローできます。
しかし、標準実装の最も重要な欠点は、検証が実行される要求処理段階です。 既に生成された応答ドキュメントの内容に関する情報なしで遅延モードがオンになっている場合でも、特定の攻撃クラスの特定のパラメーターの危険性について正しい仮定を立てることは不可能です。 たとえば、HTMLマークアップ要素を含むパラメーターがサーバーの応答に該当しない場合、XSSタイプ1の観点からその潜在的な危険性を主張するのはかなり奇妙です。 これは、SELECT値がSQLクエリで終わるかどうかを知らずにSELECT値が危険であると主張するのとほぼ同じです。 同様のロジックに従って、ASP.NET開発者は、クエリの検証に、SQL構文要素、パスパス、XPath式、および攻撃のごく一部に限定するのではなく、あらゆる言語への注入に一般的なその他の文字シーケンスの検索も含める必要がありますXSS固有のタイプ。 もちろん、このアプローチでは多くの誤検知が発生し、アプリケーション全体の検証が完全に無効になり、多くの労力をかけずにこれを実行できるツール(たとえば、 nuget.org/packages/DisableRequestValidation )が登場します。
それにもかかわらず、これらの欠点はすべて、次のセクションで説明する機会を活用することで解消できます。
5.クエリ検証機能の拡張
ASP.NET v4.0以降、開発者はクエリ検証の機能を拡張する機会があります。 標準実装を完全に再定義します。 これを実現するには、IsValidRequestStringメソッドをオーバーライドしてSystem.Web.Util.RequestValidatorクラスの継承者を作成するだけで十分です。 このメソッドは、次のリクエストパラメータを確認する必要があるときに呼び出され、次の引数を取ります。
- HttpContextコンテキスト-チェックが実行されるHTTPリクエストのコンテキスト。
- 文字列値-チェックする値。
- RequestValidationSource requestValidationSource-チェックされた値が属するソース。
- string collectionKey-ソース内のチェックされた値の名前。
- out int validationFailureIndex-危険な文字が検出された開始値またはそれ以外の場合は-1から始まるオフセット内部値を含む出力パラメーター。
たとえば、文字<%の組み合わせを使用して検証をバイパスする機能を排除するには、次の拡張機能を実装できます。
using System; using System.Web; using System.Web.Util; namespace RequestValidationExtension { public class BypassRequestValidator : RequestValidator { public BypassRequestValidator() { } protected override bool IsValidRequestString( HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) { validationFailureIndex = -1; for (var i = 0; i < value.Length; i++) { if (value[i] == '<' && (i < value.Length - 1) && value[i + 1] == '%') { validationFailureIndex = i; return false; } } return base.IsValidRequestString( context, value, requestValidationSource, collectionKey, out validationFailureIndex); } } }
次に、Webアプリケーション構成のhttpRuntimeセクションでrequestValidationType = "RequestValidationExtension.BypassRequestValidator"属性の値を設定することにより、検証を回避するこの方法から保護されているWebアプリケーションを取得します。
6.クエリ検証の改善
クエリ検証の機能を拡張する機能を使用すると、現在の実装の既存の問題をすべて排除し、本格的なXSS Type-1 WAFを取得することができます。 このため、要求パラメーターの値の検証は、応答が既に生成され、その内容がわかっている場合、応答を送信する直前に実行する必要があります。 これは、クエリパラメーターが応答の整合性に与える影響を信頼性の高い方法で評価するために必要な条件です。 ただし、ASP.NET要求検証アーキテクチャは、その機能のフレームワーク内で実行する機能を提供していないため、手順全体を3つの段階に分割する必要があります。
最初の段階の機能は、標準のクエリ検証の拡張機能で直接実装されます。 この段階で、チェック対象の各パラメーターはセット^?I [A-Za-z0-9 _] + $と一致し、比較に合格しない場合、パラメーターは後でチェック済みとしてマークされます。 したがって、チェックが要求されたすべての潜在的に危険なクエリパラメーター(つまり、要求を処理するときにWebアプリケーションで実際に使用されるパラメーター)に関する情報が収集されます。 これにより、クエリ検証の既存のアーキテクチャとの完全な統合が保証され、既知の危険なパラメータを追加チェックする必要もなくなります。
2番目の段階はASP.NET出力フィルターであり、要求ライフサイクルのさまざまな段階で形成される応答のすべてのフラグメントが生成されるストリームの監視を実装します。 応答全体の内容も、3番目のステップでの処理のために保存されます。
EndRequestイベントを処理するHTTPモジュールとして実装される3番目のステージでは、1番目のステージで収集されたすべてのパラメーターが2番目に受信した応答の整合性に与える影響を評価します。 応答の整合性の違反が検出された場合、チェックは失敗したと見なされます。 クエリパラメーターの整合性への影響の評価は、いわゆる挿入ポイント(応答フラグメントがいずれかのパラメーターの値とほぼ一致する場所)の回答におけるファジー検索に基づいています。 挿入ポイントのセットは挿入マップを形成します。これにより、パラメーター値に禁止文字が存在するかどうかのより合理的なチェックを行い、応答の整合性に対する影響の性質を明らかにすることができます。
最後のタスクは、応答(HTML、JavaScript、CSS)にあるすべての言語のパーサーを使用して解決されます。 応答のさまざまなフラグメントを挿入マップからのデータと解析して得られた解析ツリーを比較すると、1つまたは別のパラメーターの値を持つツリーのどのノードが応答に埋め込まれたかについての完全な情報が得られます。
検証アルゴリズムの詳細な説明
HTML要素の属性をチェックするためのアルゴリズム(属性Aの値、挿入マップMおよび応答テキストRを受け入れます):
クライアントシナリオのコードと属性イベントハンドラーの値をチェックするためのアルゴリズム(テスト対象のスクリプトのコードを含む入力値Vs、および交差が検出されたセットMの要素の値Vmを取得します):
スタイル定義コードをチェックするためのアルゴリズムは、CSSパーサーと他のしきい値を解析ツリーの要素と最大の共通部分文字列に使用することを除いて、前のものとまったく同じです。
- 出力にゼロバイトが含まれている場合、チェックは失敗します。
- パラメータリストPの各要素に対して、応答テキストR内のその値のすべてのオカレンスのしきい値0.75でファジー検索が実行されます。見つかった各オカレンスの境界が挿入領域を決定します。 挿入領域の多くは、挿入Mをマップします。
- Mが空の場合、チェックは合格と見なされます。
- 各要素Mについて、通常のセット<?I [az!/ \?%]と一致する値のチェックが行われます。 コンプライアンスが見つかった場合、チェックは失敗します。
- 出力テキストRは、HTMLパーサーによってツリーR 'に解析されます。
- 解析の結果としてエラーが発生し、その発生場所にMの要素との交差がある場合、チェックは失敗します。
- 後続のすべてのステップは、HTMLタグまたはコメントを記述するツリーRの各ノードNに対して繰り返されます。
- Rの初期位置NにMの要素との交差がある場合、チェックは失敗したと見なされます。
- NがHTMLタグを記述する場合、R内の位置がMの要素と交差する各属性について、以下に説明するアルゴリズムに従ってチェックが実行されます。
- Nがタグを記述する場合、そのinnerText値(スクリプトコード)については、以下で説明するアルゴリズムに従ってチェックが実行されます。
- Nがタグを記述する場合、その値innerText(スタイルを定義するためのコード)について、以下で説明するアルゴリズムに従ってチェックが実行されます。
- 前のステップで失敗しなかった場合、チェックは合格と見なされます。
HTML要素の属性をチェックするためのアルゴリズム(属性Aの値、挿入マップMおよび応答テキストRを受け入れます):
- RのAの初期位置にMの要素との交差がある場合、チェックは失敗したと見なされます。
- RのAの値の位置がMの要素と交差しない場合、チェックは合格と見なされます。
- 名前Aがイベントハンドラー属性のリストに含まれている場合、その値は以下で説明するアルゴリズムに従ってチェックされます。
- 名前Aが参照タイプの属性リストの要素と一致する場合、次の手順が実行されます。
- 値Aにサブストリング「&#」またはHTMLエンティティへの名前付きリンクが含まれている場合、チェックは失敗したと見なされます。
- 値Aに「:」が含まれていない場合、チェックは合格と見なされます。
- 値Aは、URIパーサーによってオブジェクトUに解析されます。
- 解析中にエラーが発生した場合、チェックは失敗したと見なされます。
- Uが絶対パスを記述しない場合、チェックは失敗したと見なされます。
- Uが危険なリストにある回路のパスを示している場合、チェックは失敗したと見なされます。
- 名前がA = "style"の場合、その値は以下で説明するアルゴリズムに従ってチェックされます。
クライアントシナリオのコードと属性イベントハンドラーの値をチェックするためのアルゴリズム(テスト対象のスクリプトのコードを含む入力値Vs、および交差が検出されたセットMの要素の値Vmを取得します):
- VsとVmの最大共通部分文字列Lが7未満の場合、チェックは合格と見なされます。
- JavaScriptパーサーによってVsのツリーに解析されたVs値
- 解析中にエラーが発生した場合、チェックは失敗したと見なされます。
- Vsのトークン数が「5未満」または「Vsのノード数」が2未満の場合、チェックは合格と見なされます。
- Lの値が完全にツリーVs 'の1つのトークンVtの値である場合、チェックは合格と見なされます。
- JavaScriptでデコードされたVtの値は、パラメーターVmから完全に形成された応答のテキストであるかのように、再帰的なチェックを受けます。
スタイル定義コードをチェックするためのアルゴリズムは、CSSパーサーと他のしきい値を解析ツリーの要素と最大の共通部分文字列に使用することを除いて、前のものとまったく同じです。
説明したアルゴリズムの概念実証実装は、 GitHubで利用できます 。 執筆時点では、このフィルターをバイパスする既知の方法はありません。 テストでは、リクエストに危険な値が含まれていない場合はWebアプリケーションのパフォーマンスに明確な影響はなく、それ以外の場合は応答の生成が7〜15%遅いことが示されました。 Proof-of-Conceptバージョンは、応答検証アルゴリズムのフレームワーク内で必要とされるよりもはるかに一般的な問題を解決するサードパーティのパーサーを使用することを考えると、これらのコンポーネントの最適な実装は、生産的な環境でのソリューションの信頼性の高いアプリケーションに十分なパフォーマンスを達成します
7.結論
ASP.NETの現在のバージョンでのクエリ検証機能の実装は非効率的であり、XSS Type-1クラスの攻撃に対する保護の問題を解決しません。 ただし、現在のアーキテクチャと拡張機能により、この記事で説明する応答検証方法を使用して、この問題を個別に解決できます。
ただし、このような攻撃に対する最も効果的な保護は、サードパーティが実装したソリューションではなく、開発者自身による入出力データ処理の正しい実装であることを忘れないでください。 また、Irvまたはより複雑な製品( IISや.NetIDSの mod-securityなど)を使用しても、開発者は安全なコードを開発するための基本的なルール( www.troyhunt.com/2011/12/free-ebook-owasp-topなど )に従う必要がありません-10-for-net.htmlまたはwiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines )。