ASP.NET MVCグループの開発者の1人であるBrad Wilsonは、ASP.NET MVC 2の最終バージョンで行われる概念上の変更についてブログで発表しました 。
フォームフィールドから開始する検証の概念を、データモデルに基づく概念に変更することについて話している。 この記事では、これらの変更が行われた理由と開発者にとっての意味を説明したいと思います。
デモンストレーションのために、簡単なデータモデルを定義したいと思います。 例として、単一のdiggのようなサービスレコードの単純化されたモデルを選択しました。 このようなサービスでは、各エントリはネットワーク上の有用なリソースへのリンクのテキストと、このリンクの説明です。
この記事では、画像を使用してコードを示します。 これは、複数のリソースに記事を投稿する予定であり、各リソースの構文の色付けに時間を費やしたくないという事実によるものです。 その見返りとして、記事の最後に、完成したプロジェクトとしてソースコードを添付します。だからStory.csクラス:
ここでは、UrlおよびDescriptionフィールドに加えて、フィールドId(識別子)、Approved(投稿がモデレートされたことを示すフラグ)、およびCreateDate(投稿の作成日)が宣言されています。 この場合のバインド属性は、データをモデルインスタンスにリンクするときに、ID値を関連付ける必要がないことを示します。
デモンストレーションには、入力用のフォームと結果を出力するフィールドが必要です。 次のマークアップを作成します。
何かを入力して結果を見てみましょう。
一見したところ、コードに検証がないことを除いて、すべてが問題ありません。 検証をサポートするために、DataAnnotations属性を使用してデータモデルクラスを変更します。
ここで、フォームから空のフィールドを送信しようとすると、検証エラーが発生します。
これまでのところ、すべてが美しく美しく見えます。 ただし、このオプションを想像してください。別の開発者が別のページを作成したときに、説明を入力するフィールドがフォームオプションの1つに追加されていません。 または、より悲劇的なことに、攻撃者はDescriptionフィールドを指定せずにデータを生成して送信します。 この場合、アプリケーションはどのように動作しますか?
不正なデータを送信するこのような試みを示すために、Description値ではなくUrl値を含む単純なリクエストを作成しました。 以下のリクエストの結果を見ることができます:
ご覧のとおり、Descriptionはnullですが、データモデルは有効と見なされます。 追加のチェックがない場合、このクエリは無効な(私たちの考えによる)モデルのレコードをデータベースにもたらします。
問題
キャッチは何ですか? 実際のところ、ASP.NET MVC 2で機能している検証は、現在公開されているすべてのバージョン(最新バージョンはRC)で機能し、フォームの値に基づいています。 つまり、サーバーに転送されたフィールドのみが検証の対象となり、他のフィールドは検証に合格しません。
この時点での問題は次のとおりです。サーバーの検証は、不正アクセスに対する保護メカニズムであるかどうかです。 最近まで、答えは本質的にノーでした。 検証は、特定の条件のユーザー入力をチェックする手段でした。
ただし、複雑さに詳しくないほとんどの開発者は、検証によってデータが不正アクセスから保護されると考えています。この場合、デフォルトでは、検証メカニズムが作成されたリソースの多数の脆弱性の原因になります。
解決策
引き換えに何が提供されますか? 解決策は、検証の動作をフォームからモデルに変更することです。 つまり、ほとんどの開発者が期待する動作を実現します。 ASP.NET MVC 2の最終バージョンでは、これが実行されます。検証は渡された値に依存しなくなりますが、データモデルに完全に依存します。
そのため、攻撃者は、データのない誤って生成されたリクエストを送信することで、私たちを傷つけることはできません。 データが必要であるとモデルが判断した場合、フォームの値とモデルインスタンスをバインドすると、検証エラーが発生し、モデルは無効になります。
詳細または「それほど単純ではない」
今後のイノベーションには、注意を払う必要があるいくつかのポイントがあります。
複合型
モデル内のネストされた複合オブジェクトの検証ルールは、フォームにこの複合型のフィールドが少なくとも1つある場合にのみ検証に影響します。 たとえば、AddressクラスのプロパティがPersonクラスで宣言されている場合、Addressプロパティの少なくとも1つがサーバー要求に関係している場合にのみ、Addressの検証ルールが適用されます。
バインド属性処理の変更
検証メカニズムの変更により、モデルに設定されているバインド属性の処理が変更されました。 MVC 2の最終バージョンでは、プロパティがバインド属性の除外リストに配置されているかどうかに関係なく、モデルのすべてのプロパティに対して検証が実行されます。
以前は、除外リストに特定のフィールドを設定することで、部分的なモデル検証を実現できました。 これ以降、これは不可能です。すべてのプロパティの検証ルールがチェックされます。
Null不可タイプに必須
検証はモデルに基づいているため、null不可の型にRequiredを使用しても意味がありません。 値の型にはデフォルト値があるため、フォームから値が渡されたかどうかを判断することはできません。
モデルの一部としてのパラメーター
別の考えられる問題は、開発者が厳密なプレゼンテーションモデルをアクションパラメーターとしてではなく、その一部を使用できることです。
ここで、エントリの編集は、最初に1つのidパラメータをバインドすることによって行われます。 次に、既存のレコードがリポジトリからダウンロードされ、更新が試行されます。 この問題は、サーバーに送信されるデータに必要な値がすべて含まれていない場合に発生します。 たとえば、同じ「説明」フィールドは空白です。 提示された例では、説明フィールドの検証はどのコード実行場所でも機能せず、モデルは誤って保存されます。
問題の解決策は、モデルの一部ではなく、厳密にモデルのタイプを使用することです。 その後、アクションパラメータをリンクする段階で検証が機能します。
追加のパラメーターを渡す
検証を使用するときに発生する可能性がある別の問題は、サーバーに送信すべきではないモデルフィールドの値を攻撃者が送信できることです。 これは、Storyモデルの承認された値である可能性があります。 記録の承認値は、記録がモデレートされたことを意味します。つまり、この値はシステムセキュリティパラメータを決定し、そのアクセスを明確に制限する必要があります。
フォームから悪意のあるリクエストを送信しました。結果を見てください。
モデルにはすでにApproved = trueの設定値が設定されていることがわかります。つまり、攻撃者がモデレートメカニズムを克服している可能性があります。
この状況を回避するには、検証メカニズムを慎重に検討し、モデルを正しく計画する必要があります。 たとえば、このような事前調整のハッキングを回避するには、モデルのBin属性の除外リストに値Approvedを追加する必要があります。
この場合、ユーザーデータのバインドは承認済みフィールドに対して実行されないため、問題が解決します。
おわりに
この記事では、ASP.NET MVC 2の最終バージョンで行われる検証メカニズムの革新をレビューしました。さらに、データバインディング、検証、およびフォームからのデータ送信を使用する際にセキュリティ上の問題が発生しました。
自動検証とデータバインディングは、非常に便利なメカニズムであり、人生をはるかに楽にします。 しかし、それらを使用すると、特定の安全規則を遵守する義務が課せられますが、これは忘れてはなりません。 この記事がASP.NET MVCで安全なサイトを構築するのに役立つことを願っています。 同僚たち、素晴らしい発展を!
付録:記事のソースコード