セキュリティに関するデータの考慮事項
Windows Communication Foundation (WCF) でデータを処理するとき、さまざまな種類の脅威について考慮する必要があります。 データ処理に関連する最も重要な脅威の種類を次の一覧に示します。 WCF には、これらの脅威を軽減するためのツールが用意されています。
サービス拒否
信頼できないデータを受信すると、受信側では、そのデータによって非常に長い計算が発生し、メモリ、スレッド、利用可能な接続、プロセッサ サイクルなど複数のリソースに過剰にアクセスすることがあります。 サーバーに対するサービス拒否攻撃によって、サーバーがクラッシュし、正当なクライアントからのメッセージを処理できなくなる場合があります。
悪質なコードの実行
信頼できないデータを受信すると、受信側で本来意図してしないコードが実行されます。
情報漏えい
リモートの攻撃者は、受信側に要求への応答を強制し、受信側が意図した以上の情報を公開させます。
ユーザー指定のコードとコード アクセス セキュリティ
Windows Communication Foundation (WCF) インフラストラクチャのさまざまな場所で、ユーザーが指定したコードが実行されます。 たとえば、 DataContractSerializer シリアル化エンジンでは、ユーザー指定のプロパティの set
アクセサーと get
アクセサーが呼び出されることがあります。 また、WCF チャネル インフラストラクチャで、Message クラスのユーザー指定の派生クラスが呼び出されることもあります。
コード作成者は、コードにセキュリティの脆弱性が存在しないことを確認する必要があります。 たとえば、整数型のデータ メンバー プロパティを使用してデータ コントラクト型を作成し、 set
アクセサー実装でこのプロパティ値に基づいて配列を割り当てた場合、悪質なメッセージにこのデータ メンバーの極端に大きな値が含まれていると、サービス拒否攻撃の危険にさらされることになります。 一般的に、受信データに基づく割り当てや、ユーザー指定のコードでの長時間に及ぶ処理は回避します (特に、長時間に及ぶ処理が少量の受信データによって発生する可能性がある場合)。 ユーザー指定のコードのセキュリティ分析を実行するときは、必ず失敗した場合 (つまり、例外をスローするすべてのコード分岐) も考慮してください。
ユーザー指定コードの特に重要な例は、各操作に対するサービス実装内部のコードです。 サービス実装のセキュリティを確保することはユーザーの責任です。 サービス拒否の脆弱性を生じるような、セキュリティで保護されていない操作実装は、不注意によって容易に作成されてしまいます。 たとえば、文字列を受け取り、名前がその文字列で始まる顧客のリストをデータベースから返す操作があるとします。 使用しているデータベースの規模が大きく、渡された文字列が 1 文字の場合、ユーザーのコードは、使用可能なメモリより大きなメッセージを作成しようとする可能性があります。これによって、サービス全体が失敗します (.NET Framework では OutOfMemoryException を回復できないため、アプリケーションは必ず強制終了されます)。
悪質なコードがさまざまな機能拡張ポイントに接続されないようにする必要があります。 これが特に関係するのは、部分信頼で実行する場合、部分信頼アセンブリの型を処理する場合、または部分信頼コードで使用できるコンポーネントを作成する場合です。 詳細については、後のセクションの「部分信頼に関する脅威」を参照してください。
部分信頼で実行する場合、データ コントラクトのシリアル化インフラストラクチャがサポートするのは、データ コントラクト プログラミング モデルの一部のサブセットのみです。たとえば、 SerializableAttribute 属性を使用するプライベート データ メンバーや型はサポートされません。 詳細については、「部分信頼」を参照してください。
Note
コード アクセス セキュリティ (CAS) は、.NET Framework と .NET のすべてのバージョンで非推奨になりました。 最近のバージョンの .NET では、CAS 関連の API が使われている場合、CAS の注釈は使われず、エラーが発生します。 開発者は、代わりの手段を見つけてセキュリティ タスクを実現する必要があります。
意図しない情報公開の回避
セキュリティを念頭に置いてシリアル化可能な型を設計するとき、情報の漏えい公開は考慮事項の 1 つです。
次の点を考慮します。
DataContractSerializer プログラミング モデルでは、シリアル化中に型またはアセンブリの外側で、プライベートな内部データの公開が許可されます。 さらに、スキーマのエクスポート時に型の形状が公開されることがあります。 必ず、型のシリアル化射影について理解してください。 型を公開しない場合は、シリアル化を無効にします (たとえば、データ コントラクトの場合、 DataMemberAttribute 属性を適用しないことによって無効にします)。
使用するシリアライザーによっては、同じ型に対して複数のシリアル化射影が存在することがあります。 また、同じ型であっても、 DataContractSerializer を使用したときに公開されるデータ セットと、 XmlSerializerを使用したとき公開されるデータ セットが異なる可能性があります。 使用するシリアライザーを誤ると、情報の漏えいを招く場合があります。
従来のリモート プロシージャ コール (RPC)/エンコード モードで XmlSerializer を使用すると、送信側のオブジェクト グラフの形状が誤って受信側に公開されることがあります。
サービス拒否攻撃の防止
Quotas (クォータ)
受信側で大量のメモリ割り当てが発生した場合、サービス拒否攻撃の可能性があります。 ここでは、主に大きいメッセージが原因で起こるメモリ消費の問題について説明しますが、攻撃には他の種類もあります。 たとえば、メッセージに過度の処理時間が使用される場合があります。
サービス拒否攻撃の緩和には通常、クォータを使用します。 クォータを超えると、通常は QuotaExceededException 例外がスローされます。 クォータが設定されていないと、悪質なメッセージが使用可能なすべてのメモリにアクセスし、 OutOfMemoryException 例外が発生したり、使用可能なすべてのスタックにアクセスし、 StackOverflowException例外が発生したりする可能性があります。
クォータが超過した状況は、回復可能です。実行中のサービスでこの状況が発生した場合、現在処理されているメッセージは破棄されますが、サービスの実行は続行され、次のメッセージが処理されます。 ただし、メモリ不足とスタック オーバーフローの場合は、.NET Framework のどの場所でも回復不可能です。このような例外が発生した場合、サービスは強制終了されます。
WCF のクォータには、事前割り当ては含まれません。 たとえば、さまざまなクラスにある MaxReceivedMessageSize クォータを 128 KB に設定した場合、各メッセージに 128 KB が自動的に割り当てられるわけではありません。 実際の割当量は、実際の受信メッセージのサイズによって異なります。
トランスポート層には、利用できるクォータが多数あります。 使用しているトランスポート チャネル (HTTP、TCP など) によって指定されるクォータもあります。 ここでは、これらのクォータの一部について説明しますが、詳細については、「 Transport Quotas」を参照してください。
ハッシュ テーブルの脆弱性
データ コントラクトにハッシュ テーブルまたはコレクションが含まれている場合は脆弱性が存在します。 多数の値がハッシュ テーブルに挿入されると問題が発生します。ハッシュ テーブルでは、このような多数の値によって同じハッシュ値が生成されます。 これは、DOS 攻撃に使用される可能性があります。 この脆弱性は、MaxReceivedMessageSize バインディング クォータを設定すると軽減できます。 このような攻撃を防ぐためにこのクォータを設定している場合は注意が必要です。 このクォータにより、WCF メッセージのサイズに上限が設定されます。 また、データ コントラクトではハッシュ テーブルやコレクションを使用しないようにしてください。
メモリ消費の制限 (ストリーミングなしの場合)
大きいメッセージに関するセキュリティ モデルは、ストリーミングが使用されているかどうかによって異なります。 ストリーミングを使用しない基本的なケースでは、メッセージはメモリにバッファーされます。 この場合、 MaxReceivedMessageSize またはシステム指定のバインディングで TransportBindingElement クォータを使用して、アクセスするメッセージの最大サイズを制限することによって、サイズの大きいメッセージからシステムを保護します。 サービスが複数のメッセージを同時に処理していることがありますが、この場合、メッセージはすべてメモリ内にあります。 この脅威を軽減するには、調整機能を使用します。
また、 MaxReceivedMessageSize
ではメッセージごとのメモリ消費に対して上限値が設定されませんが、メッセージごとのメモリ消費が定数係数以内に制限されます。 たとえば、 MaxReceivedMessageSize
が 1 MB のときに 1 MB のメッセージを受信し、逆シリアル化した場合、逆シリアル化されたオブジェクト グラフを格納するために追加のメモリが必要となるため、メモリの総消費量が 1 MB を超えることになります。 このため、受信するデータは大きくなくてもメモリ消費量が極端に大きくなるような、シリアル化可能な型は作成しないようにします。 たとえば、50 個の省略可能なデータ メンバー フィールドと 100 個のプライベート フィールドが設定されたデータ コントラクト "MyContract" が、"<MyContract/>" という XML 構造でインスタンス化されるとします。 この XML は、150 個のフィールドに対応するメモリにアクセスすることになります。 既定では、このデータ メンバーは省略可能です。 このような型が配列に含まれていると、問題はさらに悪化します。
MaxReceivedMessageSize
だけでは、すべてのサービス拒否攻撃を防止できません。 たとえば、受信メッセージによって、デシリアライザーが深く入れ子になったオブジェクト グラフ (オブジェクトに別のオブジェクトが格納され、その別のオブジェクトにさらに別のオブジェクトが格納されているということが繰り返されているオブジェクト) を逆シリアル化することを強制される場合があります。 DataContractSerializer と XmlSerializer は、このオブジェクト グラフを逆シリアル化するために入れ子形態でメソッドを呼び出します。 メソッド呼び出しがレベルの深い入れ子構造になっていると、回復不可能な StackOverflowExceptionが発生する可能性があります。 この脅威は、このトピックの後半の「XML の安全な使用」で説明するように、 MaxDepth クォータを設定して、XML の入れ子レベルを制限することによって軽減できます。
バイナリ XML エンコーディングを使用する場合は、追加のクォータを MaxReceivedMessageSize
に設定することが特に重要です。 バイナリ エンコーディングの使用は、圧縮に似ているところがあります。受信メッセージ内の少量のバイトで、大量のデータが表されていることがあります。 したがって、 MaxReceivedMessageSize
の制限に収まるメッセージでも、完全に展開された形式では、さらに多くのメモリを消費する可能性があります。 このような XML 固有の脅威を軽減するには、すべての XML リーダーのクォータを、このトピックの後半の「XML の安全な使用」で説明するとおりに設定する必要があります。
メモリ消費の制限 (ストリーミングありの場合)
ストリーミング中、 MaxReceivedMessageSize
の設定値を小さくすることで、サービス拒否攻撃を防止できます。 ただし、ストリーミングを伴う場合、状況が複雑になります。 たとえば、ファイル アップロード サービスでは使用可能なメモリより大きいファイルが許容されます。 この場合、メモリにバッファーされるデータはほとんどなく、メッセージが直接ディスクにストリーミングされると予測して、 MaxReceivedMessageSize
を非常に大きな値に設定します。 何らかの方法で悪質なメッセージがデータをストリーミングするのではなく、バッファーに格納することを WCF に強制した場合、メッセージが使用可能なすべてのメモリにアクセスする状況を MaxReceivedMessageSize
で防ぐことはできなくなります。
この脅威を軽減するために、WCF に含まれる複数のデータ処理コンポーネントには、バッファー化を制限するためのクォータ設定が存在します。 この中で最も重要なのは、複数のトランスポート バインド要素と標準バインディングに存在する MaxBufferSize
プロパティです。 ストリーミングを使用する場合は、メッセージごとに割り当て可能なメモリの最大量を考慮して、このクォータを設定する必要があります。 MaxReceivedMessageSize
と同様、この設定ではメモリ消費の絶対的な最大値が設定されるのではなく、メモリ消費が定数係数以内に制限されるだけです。 また、 MaxReceivedMessageSize
の場合と同じく、複数のメッセージが同時に処理されている可能性もあります。
MaxBufferSize の詳細
MaxBufferSize
プロパティは、WCF が実行するすべての大量バッファーを制限します。 たとえば、WCF では常に、SOAP ヘッダーと SOAP エラー、さらに MTOM (Message Transmission Optimization Mechanism) メッセージ内で自然な読み取り順序ではないことが検出された MIME パートがバッファー化されます。 この設定では、このようなすべての状況でバッファー量が制限されます。
WCF は、バッファー化を行う可能性がある多様なコンポーネントに MaxBufferSize
の値を渡すことによって、この制限を実現します。 たとえば、 CreateMessage クラスの一部の Message オーバーロードは、 maxSizeOfHeaders
パラメーターを受け取ります。 WCF は、このパラメーターに MaxBufferSize
の値を渡して、SOAP ヘッダーのバッファー量を制限します。 Message クラスを直接使用する場合、このパラメーターを設定することが重要です。 通常、WCF でクォータ パラメーターを受け取るコンポーネントを使用する場合、このパラメーターのセキュリティに対する影響を理解し、正しく設定することが重要です。
MTOM メッセージ エンコーダーには、 MaxBufferSize
設定もあります。 標準バインディングを使用する場合、この値は自動的にトランスポート レベルの MaxBufferSize
に設定されます。 ただし、MTOM メッセージ エンコーダーのバインド要素を使用してカスタム バインディングを作成する場合、ストリーミングの使用時には MaxBufferSize
プロパティを安全な値に設定することが重要です。
XML ベースのストリーミング攻撃
ストリーミングが予想される場合、MaxBufferSize
だけでは、WCF によるバッファー化が強制されないようにすることができません。 たとえば、WCF の XML リーダーは、新しい要素の読み取りを開始すると、必ず XML 要素の開始タグ全体をバッファー化します。 この処理は、名前空間と属性を適切に処理するために行われます。 (たとえば、大きいファイルを直接ディスクにストリーミングするシナリオを可能にする目的で) MaxReceivedMessageSize
が大きい値に設定されている場合、メッセージの本文全体が大きい XML 要素の開始タグであるような、悪質なメッセージが作成される可能性があります。 このメッセージを読み取ろうとすると、 OutOfMemoryExceptionが発生します。 これは、XML ベースのサービス拒否攻撃の 1 つです。このような XML ベースのサービス拒否攻撃はすべて、このトピックの後半の「XML の安全な使用」で説明する XML リーダーのクォータを使用して軽減できます。 ストリーミングを使用する場合、これらのクォータをすべて設定することが特に重要です。
ストリーミングとバッファー プログラミング モデルの混在
攻撃の多くは、同じサービス内にストリーミングとストリーミング以外のプログラミング モデルを混在させることによって発生することが考えられます。 2 つの操作が設定されたサービス コントラクトがあるとします。一方は Stream を使用し、他方はカスタム型の配列を使用します。 また、1 つ目の操作で、大きいストリームを処理できるように、 MaxReceivedMessageSize
が大きい値に設定されているとします。 この場合、大きいメッセージを 2 つ目の操作でも受け取れることになってしまいます。デシリアライザーは、操作が呼び出される前にデータを配列としてメモリにバッファー化します。 この状況では、サービス拒否攻撃が発生する可能性があります。 MaxBufferSize
クォータでは、メッセージ本文のサイズ、つまり、デシリアライザーが処理するデータのサイズは制限できません。
このため、同じコントラクトにストリーム ベースの操作とストリーム以外の操作を混合しないようにしてください。 この 2 つのプログラミング モデルを混合する必要がある場合は、次の予防策を使用します。
IExtensibleDataObject の IgnoreExtensionDataObject プロパティを ServiceBehaviorAttribute に設定して、
true
機能を無効にします。 これによって、コントラクトの一部であるメンバーだけが逆シリアル化されます。MaxItemsInObjectGraph の DataContractSerializer プロパティを安全な値に設定します。 このクォータは、 ServiceBehaviorAttribute 属性または構成からも操作できます。 このクォータは、1 回の逆シリアル化で逆シリアル化されるオブジェクトの数を制限します。 通常、1 回の逆シリアル化で、メッセージ コントラクト内の 1 つの操作パラメーターまたはメッセージ本文が逆シリアル化されます。 配列を逆シリアル化する場合、各配列エントリは個別のオブジェクトとしてカウントされます。
すべての XML リーダーのクォータを安全な値に設定します。 MaxDepth、 MaxStringContentLength、および MaxArrayLength に注意して、ストリーミング以外の操作で文字列の使用を避けます。
既知の型のリストを確認し、ここにある型はいつでもインスタンス化が可能であることを覚えておいてください (このトピックの後半の「意図しない型の読み込み防止」を参照)。
IXmlSerializable インターフェイスを実装して大量のデータをバッファー化する型は使用しないようにします。 このような型は既知の型のリストに追加しないでください。
XmlElement、 XmlNode 、 Byte の各配列や、 ISerializable を実装する型をコントラクトで使用しないでください。
XmlElement、 XmlNode 、 Byte の各配列や、 ISerializable を実装する型は、既知の型のリストで使用しないでください。
前述の予防策は、ストリーミング以外の操作で DataContractSerializerを使用する場合に適用できます。 XmlSerializerを使用している場合、これには MaxItemsInObjectGraph クォータによる保護がないので、同じサービスにストリーミングとストリーミング以外のプログラミング モデルを絶対に混在させないでください。
遅いストリームによる攻撃
ストリーミング サービス拒否攻撃では、メモリ消費は発生しません。 代わりに、この攻撃では送信側または受信側でデータの転送が遅くなります。 データが送信または受信されるのを待っている間、スレッドや利用可能な接続などのリソースが消耗します。 この状況は、悪質な攻撃を受けた結果として、または正当な送信側または受信側が遅いネットワーク接続を使用していることが原因で起こる可能性があります。
この攻撃を軽減するには、トランスポートのタイムアウトを適切に設定します。 詳細については、「トランスポート クォータ」を参照してください。 また、WCF でストリームを処理するときは、同期 Read
操作または同期 Write
操作を使用しないでください。
XML の安全な使用
Note
このセクションは XML に関するものですが、ここに記載された情報は、JSON (JavaScript Object Notation) ドキュメントにも該当します。 Mapping Between JSON and XMLを使用することで、クォータは同様に機能します。
セキュリティで保護された XML リーダー
XML Infoset は、WCF でのすべてのメッセージ処理の基礎となります。 信頼できない送信元からの XML データを受け入れる場合、多数のサービス拒否攻撃が存在する可能性があり、これを軽減する必要があります。 WCF には、セキュリティで保護された特別な XML リーダーが用意されています。 このリーダーは、WCF で標準エンコーディング (テキスト、バイナリ、MTOM) のいずれかを使用しているときに自動的に作成されます。
このリーダーでは、一部のセキュリティ機能が常にアクティブです。 たとえば、このリーダーは文書型定義 (DTD) を処理しません。文書型定義は、サービス拒否攻撃のソースの 1 つになり得るものであり、正当な SOAP メッセージに使用すべきではありません。 他のセキュリティ機能としてリーダーのクォータがあり、これらを設定する必要があります。この機能については、次のセクションで説明します。
XML リーダーを直接操作するとき (カスタム エンコーダーを記述するときや、Message クラスを直接操作するときなど)、信頼できないデータを処理する可能性がある場合は必ず、WCF のセキュリティで保護されたリーダーを使用します。 セキュリティで保護されたリーダーは、 CreateTextReaderクラス上で CreateBinaryReader、 CreateMtomReader 、または XmlDictionaryReader の静的ファクトリ メソッドのオーバーロードのいずれかを呼び出すことによって作成します。 リーダーを作成したら、安全なクォータ値を渡します。 Create
メソッドのオーバーロードを呼び出さないでください。 オーバーロードを呼び出しても WCF のリーダーは作成されません。 作成されるのは、このセクションで説明したセキュリティ機能で保護されていないリーダーです。
リーダーのクォータ
セキュリティで保護された XML リーダーには、5 つの設定可能なクォータがあります。 これらのクォータは通常、標準バインディングまたはエンコーディング バインド要素で ReaderQuotas
プロパティを使用するか、リーダーの作成時に渡される XmlDictionaryReaderQuotas オブジェクトを使用して設定します。
MaxBytesPerRead
このクォータは、要素の開始タグとその属性を読み取るときに、1 回の Read
操作で読み取るバイト数を制限します (ストリーミングを使用しない場合、要素名自体がクォータに照らし合わせてカウントされることはありません)。 MaxBytesPerRead が重要な理由は、次のとおりです。
要素名とその属性は、読み取り時に必ずメモリ内でバッファー化されます。 このため、ストリーミングが予想されるときにこのクォータをストリーミング モードで適切に設定して、過度のバッファーを防止することが重要です。 実行されるバッファリングの実際の量については、
MaxDepth
クォータのセクションを参照してください。XML 属性が多すぎると、属性名は一意かどうかを確認する必要があるため、処理時間が大幅に増加する可能性があります。
MaxBytesPerRead
によってこの脅威を軽減できます。
MaxDepth
このクォータは、XML 要素の入れ子の深さの最大値を制限します。 たとえば、ドキュメント "<A><B><C/></B></A>" には 3 レベルの入れ子があります。 MaxDepth が重要な理由は、次のとおりです。
MaxDepth
はMaxBytesPerRead
と相互に関係しています。リーダーは常に、現在の要素とそのすべての先祖に関するデータをメモリ内に維持します。このため、リーダーのメモリ消費の最大値は、この 2 つの設定値の積に比例します。深く入れ子になったオブジェクト グラフを逆シリアル化する場合、デシリアライザーはスタック全体にアクセスし、回復不可能な StackOverflowExceptionをスローするしかありません。 DataContractSerializer の場合も XmlSerializerの場合も、XML の入れ子構造とオブジェクトの入れ子構造との間に直接的な相関関係が存在します。 この脅威を軽減するには、
MaxDepth
を使用します。
MaxNameTableCharCount
このクォータは、リーダーの nametableのサイズを制限します。 nametable には、XML ドキュメントを処理するときに出現する特定の文字列 (名前空間やプレフィックスなど) が入っています。 この文字列はメモリ内でバッファー化されるので、ストリーミングが予想されるときは、このクォータを設定して過度のバッファーを防止します。
MaxStringContentLength
このクォータは、XML リーダーが返す文字列の最大サイズを制限します。 このクォータは、XML リーダー自体のメモリ消費は制限しませんが、このリーダーを使用するコンポーネントのメモリ消費を制限します。 たとえば、 DataContractSerializer が MaxStringContentLengthでセキュリティ保護されたリーダーを使用するときは、このクォータを超える文字列を逆シリアル化することはありません。 XmlDictionaryReader クラスを直接使用すると、すべてのメソッドではなく、文字列を読み取ることを目的としたメソッド ( ReadContentAsString メソッドなど) だけがこのクォータに従います。 リーダー上の Value プロパティはこのクォータの影響を受けないので、このクォータによる保護が必要な場合はこのプロパティを使用しないでください。
MaxArrayLength
このクォータは、XML リーダーが返すプリミティブ配列 (バイト配列など) の最大サイズを制限します。 このクォータは、XML リーダー自体のメモリ消費は制限しませんが、このリーダーを使用するコンポーネントのメモリ消費を制限します。 たとえば、 DataContractSerializer が MaxArrayLengthでセキュリティ保護されたリーダーを使用するときは、このクォータを超えるバイト配列を逆シリアル化することはありません。 1 つのコントラクトでストリーミングとバッファー プログラミング モデルを混合する場合は、このクォータを設定することが重要です。 XmlDictionaryReader クラスを直接使用すると、特定のプリミティブ型の任意のサイズの配列を読み取ることを目的としたメソッド ( ReadInt32Arrayメソッドなど) だけがこのクォータに従います。
バイナリ エンコーディングに固有の脅威
WCF がサポートするバイナリ XML エンコーディングには、"ディクショナリ文字列" 機能があります。 長い文字列をわずかなバイト数でエンコードすることができます。 これによって、パフォーマンスは大幅に向上しますが、新たなサービス拒否攻撃の脅威を招き、その対策が必要になります。
ディクショナリには、 静的ディクショナリ と 動的ディクショナリの 2 種類があります。 静的ディクショナリは、バイナリ エンコーディングで短いコードを使用して表現できる長い文字列の組み込みリストです。 この文字列のリストは、リーダーの作成時に固定され、変更できません。 WCF が既定で使用する静的ディクショナリ内の文字列はいずれも、重大なサービス拒否攻撃の脅威を招くほど長くありません。ただし、ディクショナリ拡大攻撃に使用される可能性はあります。 独自の静的ディクショナリを使用する高度なシナリオでは、長いディクショナリ文字列を追加するときに注意が必要です。
動的ディクショナリ機能では、メッセージで独自の文字列を定義し、その文字列を短いコードに関連付けることができます。 この文字列とコードのマッピングは、通信セッション中にメモリに格納されるので、後続のメッセージは文字列を再送信する必要がなく、既に定義されているコードを利用できます。 この文字列は、任意の長さになるので、静的ディクショナリの場合よりも重大な脅威を招く可能性があります。
軽減する必要がある第 1 の脅威は、動的ディクショナリ (文字列とコードのマッピング テーブル) が大きくなりすぎる可能性です。 このディクショナリは、複数のメッセージを処理するうちに拡大する可能性があります。MaxReceivedMessageSize
クォータは各メッセージに個別に適用されるだけなので、このクォータで保護することはできません。 このため、 MaxSessionSize にはディクショナリのサイズを制限する個別の BinaryMessageEncodingBindingElement プロパティがあります。
他のほとんどのクォータと異なり、このクォータはメッセージの書き込み時にも適用されます。 メッセージの読み取り時にこのクォータを超えた場合は、 QuotaExceededException
が通常どおりスローされます。 メッセージの書き込み時にこのクォータを超えた場合は、クォータの超過を引き起こす文字列はそのまま書き込まれますが、動的ディクショナリ機能は使用されません。
ディクショナリ拡大攻撃
重大なバイナリ固有の攻撃は、ディクショナリの拡大が原因で発生します。 文字列ディクショナリ機能を利用した場合、バイナリ形式の小さいメッセージが、完全に展開されたテキスト形式では非常に大きいメッセージになることがあります。 動的ディクショナリの文字列がディクショナリ全体の最大サイズを超えることはないため、動的ディクショナリの文字列の拡大要因は、 MaxSessionSize クォータで制限できます。
MaxNameTableCharCount、 MaxStringContentLength
、および MaxArrayLength
の各プロパティで制限されるのは、メモリ消費だけです。 メモリ使用量は MaxReceivedMessageSize
によって既に制限されているため、通常、ストリーミングを使用しない状況で脅威を軽減するためにこれらのプロパティを使用する必要はありません。 ただし、 MaxReceivedMessageSize
では、展開前のバイト数がカウントされます。 バイナリ エンコーディングを使用しているとき、メモリ消費は、 MaxReceivedMessageSize
という因数によってのみ制限される、 MaxSessionSizeを超える可能性があります。 このため、バイナリ エンコーディングを使用するときはすべてのリーダーのクォータ (特に MaxStringContentLength) を必ず設定することが重要です。
DataContractSerializerと共にバイナリ エンコーディングを使用する場合、 IExtensibleDataObject
インターフェイスを誤用して、ディクショナリ拡大攻撃を引き起こす可能性があります。 このインターフェイスは、コントラクトの一部でない任意のデータに対して無制限のストレージを提供します。 MaxSessionSize
に MaxReceivedMessageSize
を掛けた値によって問題が起こらない程度に低くクォータを設定できない場合は、バイナリ エンコーディングを使用するときに IExtensibleDataObject
機能を無効にします。 IgnoreExtensionDataObject
属性では true
プロパティを ServiceBehaviorAttribute
に設定します。 または IExtensibleDataObject
インターフェイスを実装しないという方法もあります。 詳細については、「上位互換性のあるデータ コントラクト」を参照してください。
クォータの概要
次の表では、クォータを使用するときのヒントを示します。
条件 | 設定が重要なクォータ |
---|---|
ストリーミングのない、またはストリーミングのある小さいメッセージ、テキスト、MTOM エンコーディング | MaxReceivedMessageSize 、MaxBytesPerRead 、MaxDepth |
ストリーミングのない、またはストリーミングのある小さいメッセージ、バイナリ エンコーディング | MaxReceivedMessageSize 、 MaxSessionSize 、およびすべての ReaderQuotas |
大きいストリーミング メッセージ、テキスト、MTOM エンコーディング | MaxBufferSize およびすべての ReaderQuotas |
大きいストリーミング メッセージ、バイナリ エンコーディング | MaxBufferSize 、 MaxSessionSize 、およびすべての ReaderQuotas |
トランスポート レベルのタイムアウトを常に設定する必要があります。ストリーミングを使用するときは、ストリーミングするメッセージが大きいか小さいかに関係なく、同期読み取りと書き込みを使用しないでください。
クォータについて確信がない場合は、そのクォータを未決定のままにするのではなく、安全な値を設定します。
悪質なコードの実行の防止
主に、次の種類の脅威が、コードを実行し、意図しない結果をもたらす可能性があります。
デシリアライザーが、悪質、安全でない、またはセキュリティに注意する必要がある型を読み込んでしまう。
受信メッセージが原因で、デシリアライザーが通常は安全な型のインスタンスを作成する際に、意図しない結果を引き起こすようなインスタンスが生成されてしまう。
次の各セクションで、これらの種類の脅威について詳しく説明します。
DataContractSerializer
(XmlSerializer のセキュリティ情報については、関連ドキュメントを参照してください)。XmlSerializer のセキュリティ モデルは、DataContractSerializer のセキュリティ モデルに似ていますが、細かい部分が異なります。 たとえば、型を含めるために、 XmlIncludeAttribute 属性ではなく KnownTypeAttribute 属性が使用されます。 XmlSerializer に固有のいくつかの脅威については、このトピックの後半で説明します。
意図しない型の読み込み防止
意図しない型を読み込むと、その型が悪質である場合も、単にセキュリティに影響するような副作用がある型の場合も、重大な結果を引き起こす可能性があります。 型は、悪用されやすいセキュリティの脆弱性を含んでいたり、その型のコンストラクターまたはクラス コンストラクターでセキュリティに影響するアクションを実行したりする可能性があります。また、サービス拒否攻撃の実行を容易にしてしまうほどメモリ フットプリントが大きい場合や、回復不可能な例外をスローする場合もあります。 型には、インスタンスが作成されるよりも前に、その型が読み込まれると直ちに実行されるクラス コンストラクターが用意されている可能性があります。 これらの理由から、デシリアライザーがどのような型を読み込むかを制御することが重要です。
DataContractSerializer での逆シリアル化は、疎結合的な方法で行われます。 共通言語ランタイム (CLR) 型とアセンブリ名は、受信データから読み取られません。 これは、 XmlSerializerの動作と似ていますが、 NetDataContractSerializer、 BinaryFormatter、および SoapFormatterの動作とは異なります。 疎結合的な方法をとることによって、ある程度の安全を確保できます。リモートの攻撃者は、メッセージ内で型の名前を示すだけでは、意図した型が読み込まれるように指定することができません。
DataContractSerializer は常に、コントラクトに基づいて現在予期される型を読み込むことができます。 たとえば、データ コントラクトに Customer
型のデータ メンバーが含まれている場合、 DataContractSerializer は、このデータ メンバーを逆シリアル化するときに Customer
型を読み込むことができます。
また、 DataContractSerializer はポリモーフィズムをサポートします。 データ メンバーを Objectとして宣言しておき、受信データに Customer
インスタンスを含めることができます。 これが可能なのは、次のいずれかのメカニズムにより、 Customer
型がデシリアライザーに対して既知の型になっている場合だけです。
型にKnownTypeAttribute 属性を適用する。
KnownTypeAttribute
属性で、型のリストを返すメソッドを指定する。ServiceKnownTypeAttribute
属性を使用する。KnownTypes
構成セクションを使用する。構築時に DataContractSerializer に既知の型のリストを明示的に渡す (シリアライザーを直接使用している場合)。
これらのメカニズムを使用すると、デシリアライザーが読み込むことのできる型が追加されるので、攻撃を受け得る表面積は拡大します。 既知の型リストに、悪質な型または意図しない型が追加されることのないように、これらのメカニズムを制御する必要があります。
型が既知の型リストに追加されると、コントラクトでその型の使用が禁止されている場合でも、その型をいつでも読み込むことができ、その型のインスタンスを作成できるようになります。 たとえば、前述のメカニズムのいずれかを使用して "MyDangerousType" という型が既知の型のリストに追加されるとします。 これは、次のことを意味します。
MyDangerousType
が読み込まれ、そのクラス コンストラクターが実行されます。文字列データ メンバーが指定されたデータ コントラクトを逆シリアル化している場合でも、悪質なメッセージによって
MyDangerousType
のインスタンスが作成される可能性があります。 プロパティの setter など、MyDangerousType
のコードが実行される可能性があります。 その後で、デシリアライザーが文字列データ メンバーにこのインスタンスを割り当てる動作を試行し、例外が発生して失敗します。
既知の型のリストを返すメソッドを作成する場合、またはリストを直接 DataContractSerializer コンストラクターに渡す場合は、リストを準備するためのコードをセキュリティで保護し、信頼されたデータだけを操作対象にします。
構成で既知の型を指定する場合は、構成ファイルをセキュリティで保護します。 構成では必ず厳密な名前を使用します (型が含まれている署名付きアセンブリの公開キーを指定します)。ただし、読み込む型のバージョンは指定しないでください。 型ローダーにより、可能であれば最新のバージョンが自動的に選択されます。 セキュリティの脆弱性を持つ型の場合、その脆弱性が今後のバージョンで修正される可能性があります。しかし、構成でバージョンを明示的に指定していると、脆弱なバージョンが引き続き読み込まれます。構成で特定のバージョンを指定した場合には、このリスクを負うことになります。
既知の型が多すぎると、もう 1 つ問題が生じます。 DataContractSerializer は、アプリケーション ドメインでシリアル化コードまたは逆シリアル化コードのキャッシュを作成し、シリアル化または逆シリアル化する必要がある型ごとにエントリを作成します。 このキャッシュは、アプリケーション ドメインの実行中はクリアされません。 このため、アプリケーションに多くの既知の型が使用されていることを知る攻撃者は、これらの既知の型がすべて逆シリアル化され、キャッシュで大量のメモリが消費されるように仕向けることが可能です。
型の意図しない状態の防止
型には、従う必要がある内部の一貫性に対する制約が存在することがあります。 逆シリアル化中にこの制約に違反しないように注意する必要があります。
次の型は、宇宙船でのエアロックの状態を表します。この型では、内側のドアと外側のドアを両方同時に開くことはできないという制約が適用されます。
[DataContract]
public class SpaceStationAirlock
{
[DataMember]
private bool innerDoorOpenValue = false;
[DataMember]
private bool outerDoorOpenValue = false;
public bool InnerDoorOpen
{
get { return innerDoorOpenValue; }
set
{
if (value & outerDoorOpenValue)
throw new Exception("Cannot open both doors!");
else innerDoorOpenValue = value;
}
}
public bool OuterDoorOpen
{
get { return outerDoorOpenValue; }
set
{
if (value & innerDoorOpenValue)
throw new Exception("Cannot open both doors!");
else outerDoorOpenValue = value;
}
}
}
<DataContract()> _
Public Class SpaceStationAirlock
<DataMember()> Private innerDoorOpenValue As Boolean = False
<DataMember()> Private outerDoorOpenValue As Boolean = False
Public Property InnerDoorOpen() As Boolean
Get
Return innerDoorOpenValue
End Get
Set(ByVal value As Boolean)
If (value & outerDoorOpenValue) Then
Throw New Exception("Cannot open both doors!")
Else
innerDoorOpenValue = value
End If
End Set
End Property
Public Property OuterDoorOpen() As Boolean
Get
Return outerDoorOpenValue
End Get
Set(ByVal value As Boolean)
If (value & innerDoorOpenValue) Then
Throw New Exception("Cannot open both doors!")
Else
outerDoorOpenValue = value
End If
End Set
End Property
End Class
攻撃者は、次のような悪質なメッセージを送信することによってこの制約を迂回し、オブジェクトを無効な状態にする可能性があります。これにより、意図しない予測不可能な結果を招くおそれがあります。
<SpaceStationAirlock>
<innerDoorOpen>true</innerDoorOpen>
<outerDoorOpen>true</outerDoorOpen>
</SpaceStationAirlock>
このような状況は、次の点を知っていると防止できます。
DataContractSerializer で逆シリアル化が行われる際に、ほとんどのクラスで、コンストラクターが実行されません。 このため、コンストラクターで実行される状態管理には依存できません。
コールバックを使用して、オブジェクトを確実に有効な状態にします。 OnDeserializedAttribute 属性でマークされたコールバックは、逆シリアル化の完了後に実行され、全体の状態を調査して訂正できるので便利です。 詳細については、「バージョン トレラントなシリアル化コールバック」を参照してください。
データ コントラクト型は、プロパティの setter が特定の順序で呼び出されなくてもよいように設計します。
SerializableAttribute 属性でマークされた従来の型を使用する場合は十分に注意します。 このような型のほとんどは、信頼されたデータのみで使用するために、.NET Framework リモート処理で操作することを目的に設計されています。 この属性でマークされた既存の型は、状態の安全性を考えて設計されていない可能性があります。
状態の安全性に関しては、データの存在を保証するために、 IsRequired 属性の DataMemberAttribute プロパティに依存することはできません。 データは常に
null
、zero
、またはinvalid
になります。信頼できないデータ ソースから逆シリアル化されたオブジェクト グラフは、検証せずに信頼してはいけません。 各オブジェクトが整合状態にあっても、オブジェクト グラフ全体としては整合状態にない場合があります。 さらに、オブジェクト グラフの保存モードが無効になっている場合でも、逆シリアル化されたグラフに、同じオブジェクトへの複数の参照または循環参照が存在することがあります。 詳細については、「シリアル化と逆シリアル化」を参照してください。
NetDataContractSerializer の安全な使用
NetDataContractSerializer は、型に対して密結合を使用するシリアル化エンジンです。 これは、 BinaryFormatter および SoapFormatterに類似しています。 つまり、このシリアル化エンジンでは、受信データから .NET Framework アセンブリと型名を読み取って、インスタンス化する型を決定します。 このシリアル化エンジンは WCF の一部ですが、このエンジンにプラグインする方法は用意されていないため、カスタム コードを記述する必要があります。 NetDataContractSerializer
は、主に、.NET Framework リモート処理から WCF への移行を容易にするためのものです。 詳細については、「シリアル化と逆シリアル化」の関連するセクションを参照してください。
読み込むことができる型はメッセージ自身が示すことができるため、 NetDataContractSerializer のメカニズムは本質的にセキュリティで保護されていません。このエンジンでは、信頼されたデータだけを使用してください。 詳細については、「BinaryFormatter セキュリティ ガイド」を参照してください。
信頼されたデータを使用している場合でも、特に AssemblyFormat プロパティが Simpleに設定されている場合は、読み込む型が受信データで適切に指定されないことがあります。 アプリケーションのディレクトリまたはグローバル アセンブリ キャッシュにアクセスできればだれにでも、読み込むべき型の代わりに、悪質な型を配置することができます。 アクセス許可を適切に設定することによって、アプリケーション ディレクトリとグローバル アセンブリ キャッシュのセキュリティを必ず確保してください。
通常、部分信頼コードで NetDataContractSerializer
インスタンスにアクセスしたり、サロゲート セレクター (ISurrogateSelector) やシリアル化バインダー (SerializationBinder) を制御したりできるようにすると、コードによってシリアル化と逆シリアル化のプロセスが細かく制御される可能性があります。 たとえば、コードによって任意の型が挿入されたり、情報漏えいにつながったりする場合があります。また、生成されたオブジェクト グラフやシリアル化されたデータが改ざんされたり、生成されたシリアル化ストリームをオーバーフローさせたりする場合もあります。
NetDataContractSerializer
に関するもう 1 つのセキュリティの問題は、悪質なコードの実行ではなく、サービス拒否攻撃です。 NetDataContractSerializer
を使用する場合は、必ず MaxItemsInObjectGraph クォータを安全な値に設定してください。 サイズがこのクォータでしか制限されないようなオブジェクトであれば、そのオブジェクトの配列を割り当てるための悪質な小さいメッセージを作成することは容易です。
XmlSerializer 固有の脅威
XmlSerializer のセキュリティ モデルは、 DataContractSerializerのセキュリティ モデルに似ています。 ただし、 XmlSerializerに固有の脅威がいくつか存在します。
XmlSerializer は、実際にシリアル化と逆シリアル化を実行するコードが含まれた "シリアル化アセンブリ" を実行時に生成します。これらのアセンブリは、ファイルの一時ディレクトリに作成されます。 他のプロセスまたはユーザーがこのディレクトリにアクセスできる場合は、シリアル化または逆シリアル化のコードが任意のコードで上書きされる可能性があります。 その結果、シリアル化または逆シリアル化のコードではなく、上書き後のコードのセキュリティ コンテキストを使用して、 XmlSerializer がこのコードを実行します。 一時ファイルのディレクトリへのアクセス許可が適切に設定されていることを確認し、この状況が起こらないようにしてください。
XmlSerializer には、シリアル化アセンブリを実行時に生成するのではなく、事前生成済みのシリアル化アセンブリを使用するモードもあります。 このモードは、 XmlSerializer が適切なシリアル化アセンブリを検出できるとトリガーされます。 XmlSerializer は、シリアル化アセンブリの署名に使用されているキーが、シリアル化する型を含むアセンブリの署名に使用されたものと同じであるかどうかをチェックします。 このチェックによって、シリアル化アセンブリに偽装した悪質なアセンブリからの保護が可能になります。 ただし、シリアル化可能な型を含むアセンブリが署名されていない場合、 XmlSerializer はこのチェックを実行できず、正しい名前を持つ任意のアセンブリを使用することになります。 これによって、悪質なコードの実行が生じる可能性があります。 シリアル化可能な型が属するアセンブリに必ず署名するか、アプリケーション ディレクトリとグローバル アセンブリ キャッシュへのアクセスを厳密に制御することによって、悪質なアセンブリの侵入を防止します。
XmlSerializer は、サービス拒否攻撃にもさらされる可能性があります。 XmlSerializer には、( MaxItemsInObjectGraph
で利用できるような) DataContractSerializerクォータがありません。 したがって、逆シリアル化されるオブジェクトの量は、メッセージ サイズによってのみ制限されるため、不定になります。
部分信頼に関する脅威
部分信頼で実行されているコードに関連する脅威について、以下の懸念事項に注意してください。 これらの脅威には、悪質な部分信頼コードと、他の攻撃シナリオと結びついた悪質な部分信頼コード (特定の文字列を作成し、それを逆シリアル化する部分信頼コードなど) があります。
シリアル化コンポーネントを使用するときは、使用前にどのアクセス許可もアサートしないでください。これは、シリアル化シナリオ全体がアサートのスコープ内であり、信頼できないデータまたはオブジェクトは処理しない場合も同様です。 アクセス許可をアサートすると、セキュリティ上の脆弱性の原因となる場合があります。
部分信頼コードで拡張ポイント (サロゲート)、シリアル化する型、または他の方法によってシリアル化プロセスを制御している場合、部分信頼コードが原因でシリアライザーが大量のデータをシリアル化ストリームに出力し、このストリームの受信側に対してサービス拒否 (DoS: Denial of Service) を引き起こす可能性があります。 DoS の脅威の影響を受けやすいターゲットを対象とするデータをシリアル化する場合、部分信頼の型をシリアル化したり、部分信頼コードでシリアル化を制御したりしないでください。
部分信頼コードで DataContractSerializer インスタンスにアクセスしたり、データ コントラクト サロゲートを制御したりできるようにすると、部分信頼コードによってシリアル化と逆シリアル化のプロセスが細かく制御される可能性があります。 たとえば、コードによって任意の型が挿入されたり、情報漏えいにつながったりする場合があります。また、生成されたオブジェクト グラフやシリアル化されたデータが改ざんされたり、生成されたシリアル化ストリームをオーバーフローさせたりする場合もあります。 NetDataContractSerializer に関する同等の脅威については、「NetDataContractSerializer の安全な使用」で説明しています。
DataContractAttribute 属性が型 (または SerializableAttribute ではなく ISerializableとマークされた型) に適用されている場合、すべてのコンストラクターが非パブリックであるか、または要求で保護されている場合でも、デシリアライザーはこのような型のインスタンスを作成できます。
逆シリアル化されるデータが信頼できるものであり、すべての既知の型が信頼した型であることが確実である場合を除き、逆シリアル化の結果を信頼しないでください。 部分信頼で実行されている場合、既知の型はアプリケーション構成ファイルから読み込まれません (ただし、コンピューター構成ファイルからは読み込まれます)。
部分信頼コードに追加されたサロゲートと共に DataContractSerializer インスタンスを渡すと、コードによってそのサロゲートの変更可能な設定が変更される可能性があります。
逆シリアル化されたオブジェクトでは、XML リーダー (またはそれに含まれるデータ) が部分信頼コードからのものである場合、結果の逆シリアル化されたオブジェクトを信頼できないデータとして扱ってください。
ExtensionDataObject 型がパブリック メンバーを含んでいなくても、それに含まれるデータが安全であるとは限りません。 たとえば、特権が付与されたデータ ソースから、いくつかのデータを含むオブジェクトに逆シリアル化し、そのオブジェクトを部分信頼コードに渡した場合、部分信頼コードは、オブジェクトをシリアル化して
ExtensionDataObject
のデータを読み取ることができます。 特権が付与されたデータ ソースから、後で部分信頼コードに渡されるオブジェクトに逆シリアル化する場合は、 IgnoreExtensionDataObject をtrue
に設定するよう考慮してください。完全信頼では、DataContractSerializer および DataContractJsonSerializer support the serialization of private, protected, internal, および public members in full trust. 一方、部分信頼ではパブリック メンバーのシリアル化のみが可能です。 アプリケーションがパブリック メンバー以外のメンバーのシリアル化を試みると、 SecurityException がスローされます。
部分信頼で内部メンバーまたはプロテクト内部メンバーをシリアル化するには、 InternalsVisibleToAttribute アセンブリ属性を使用します。 この属性を使用すると、アセンブリは内部メンバーが他のアセンブリから参照できることを宣言できます。 この場合、内部メンバーのシリアル化が必要なアセンブリは、その内部メンバーが System.Runtime.Serialization.dll から参照できることを宣言します。
この方法の利点は、昇格されたコード生成パスが不要なことです。
ただし、2 つの大きな欠点もあります。
1 つ目の欠点は、 InternalsVisibleToAttribute 属性のオプトイン プロパティがアセンブリ全体に適用されることです。 つまり、特定のクラスに対してのみ内部メンバーのシリアル化を可能にするような指定はできません。 もちろん、 DataMemberAttribute 属性をメンバーに追加しないことで、特定の内部メンバーをシリアル化しないことは可能です。 同様に、メンバーをプライベート メンバーまたはプロテクト メンバーではなく内部メンバーにすることもできますが、参照可能範囲の問題が残ります。
2 つ目の欠点は、プライベート メンバーやプロテクト メンバーがサポートされないことです。
部分信頼での InternalsVisibleToAttribute 属性の使用を理解するために、次のプログラムについて考えてみましょう。
public class Program { public static void Main(string[] args) { try { // PermissionsHelper.InternetZone corresponds to the PermissionSet for partial trust. // PermissionsHelper.InternetZone.PermitOnly(); MemoryStream memoryStream = new MemoryStream(); new DataContractSerializer(typeof(DataNode)). WriteObject(memoryStream, new DataNode()); } finally { CodeAccessPermission.RevertPermitOnly(); } } [DataContract] public class DataNode { [DataMember] internal string Value = "Default"; } }
上の例で、
PermissionsHelper.InternetZone
は部分信頼の PermissionSet に対応します。 ここで、InternalsVisibleToAttribute属性がないと、パブリック メンバー以外のメンバーは部分信頼でシリアル化できないことを示す SecurityException がスローされてアプリケーションは失敗します。ただし、ソース ファイルに次の行を追加すると、プログラムは正常に実行されます。
[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("System.Runtime.Serialization, PublicKey = 00000000000000000400000000000000")]
状態管理に関するその他の注意事項
オブジェクトの状態管理に関してその他の注意すべき点を以下に示します。
ストリーミング トランスポートでストリーム ベースのプログラミング モデルを使用した場合、メッセージが到着するとメッセージの処理が開始されます。 メッセージの送信側によってストリームの途中で送信操作が中止されることはあり得ますが、このとき続きのコンテンツを待機していた場合、コードの状態を予想できなくなります。 一般に、ストリームは完全であると考えないでください。また、ストリーミングが中止されたときにロールバックできないようなストリーム ベースの操作では、処理を行わないでください。 このことは、ストリーミングの本文の後に続くメッセージの形式が正しくない可能性があるような状況にも当てはまります (たとえば、SOAP エンベロープの終了タグが不足している、または 2 つ目のメッセージ本文があるなど)。
IExtensibleDataObject
機能を使用すると、機密データが出力される可能性があります。IExtensibleObjectData
を含むデータ コントラクトに、信頼できない送信元のデータを受け入れ、その後、メッセージが署名される、セキュリティで保護されたチャネルにそのデータを再送した場合、一切確認していないデータを保証していることになります。 さらに、既知のデータと不明なデータの両方を考慮すると、送信している全体の状態が無効になる可能性があります。 このような状況を回避するには、拡張データ プロパティをnull
に設定するか、IExtensibleObjectData
機能を無効にします。
スキーマのインポート
通常、型を生成するためにスキーマをインポートするプロセスは、設計時にのみ発生します。たとえば、クライアント クラスを生成するために Web サービス上で ServiceModel Metadata Utility Tool (Svcutil.exe) を使用しているときなどです。 ただし、さらに高度なシナリオでは、スキーマを実行時に処理することがあります。 この場合、サービス拒否攻撃の危険にさらされる可能性があることを覚えておく必要があります。 スキーマによっては、インポートに時間がかかる場合があります。 スキーマが信頼できないソースからのものである可能性がある場合は、このようなシナリオで XmlSerializer のスキーマ インポート コンポーネントを使用しないでください。
ASP.NET AJAX 統合に固有の脅威
ユーザーが WebScriptEnablingBehavior または WebHttpBehaviorを実装すると、WCF は XML メッセージと JSON メッセージの両方を受け入れることができるエンドポイントを公開します。 ただし、XML リーダーと JSON リーダーの両方が使用するリーダー クォータのセットは 1 つしかありません。 一部のクォータ設定が一方のリーダーには適切であっても、もう一方のリーダーには大きすぎる場合があります。
WebScriptEnablingBehavior
を実装すると、ユーザーはエンドポイントで JavaScript プロキシを公開するオプションを使用できます。 セキュリティに関する次の問題を考慮する必要があります。
JavaScript プロキシを調べることで、サービスに関する情報 (操作名、パラメーター名など) を取得できます。
JavaScript エンドポイントを使用すると、機密性の高い情報やプライベートな情報がクライアントの Web ブラウザーのキャッシュに保持される可能性があります。
コンポーネントに関する注意事項
WCF は、柔軟でカスタマイズ可能なシステムです。 このトピックのほとんどの内容は、最も一般的な WCF の使用シナリオに基づいています。 ただし、WCF が提供するコンポーネントは、複数の異なる方法で構築できます。 各コンポーネントを使用した場合のセキュリティへの影響について理解することが重要です。 特に次の点に違いがあります。
XML リーダーを使用する必要があるときは、 XmlDictionaryReader クラスに用意されたリーダーを使用し、それ以外のリーダーは使用しないようにします。 安全なリーダーは、 CreateTextReader、 CreateBinaryReader、 CreateMtomReader のいずれかのメソッドを使用して作成できます。 Create メソッドは使用しないでください。 リーダーは、必ず安全なクォータを使用して設定します。 WCF のシリアル化エンジンがセキュリティで保護されるのは、WCF で提供されるセキュリティで保護された XML リーダーを使用する場合だけです。
DataContractSerializer を使用して、信頼できない可能性のあるデータを逆シリアル化する場合は、必ず MaxItemsInObjectGraph プロパティを設定します。
メッセージを作成する場合、
maxSizeOfHeaders
による保護が十分でないときはMaxReceivedMessageSize
パラメーターを設定します。エンコーダーを作成する場合、
MaxSessionSize
やMaxBufferSize
などの関連クォータを必ず設定します。XPath メッセージ フィルターを使用する場合は、 NodeQuota を設定してそのフィルターがアクセスする XML ノードの量を制限します。 多数のノードにアクセスせず、計算に長い時間がかかる可能性がある XPath 式は使用しないようにします。
通常、クォータを受け入れるコンポーネントを使用する場合は、そのセキュリティへの影響を理解し、クォータを安全な値に設定します。