web-dev-qa-db-ja.com

Catch-22は、ストリーミングされたTCP WCFサービスがWIFによってセキュリティ保護可能になり、私のクリスマス、メンタルヘルスを台無しにします

WIF を使用して、ストリーミングされたWCF net.tcpサービスエンドポイントを保護する必要があります。トークンサーバーに対して着信呼び出しを認証する必要があります。大量のデータを転送するように設計されているため、サービスはストリーミングされます。

これは不可能と思われる。そして、獲物を逃れることができなければ、私のクリスマスは台無しになり、陽気な買い物客が私の足を踏んでいる間、私はガターで死ぬまで飲むゆっくりと体を冷却します。深刻なトートバッグ、皆さん。

なぜこれが不可能なのですか?これがCatch-22です。

クライアントで、トークンサーバーから取得した GenericXmlSecurityToken でチャネルを作成する必要があります。問題ありません。

// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();

「問題ありません」と言いましたか?問題。実際、NullReferenceExceptionスタイルの問題です。

「ブロ」、私はフレームワークに尋ねました、「あなたはヌルチェックさえしますか?」フレームワークは沈黙していたので、分解して、

((IChannel)(object)tChannel).
    GetProperty<ChannelParameterCollection>().
    Add(federatedClientCredentialsParameter);

例外の原因であり、GetProperty呼び出しがnullを返していました。それで、WTF?メッセージセキュリティを有効にし、クライアントの資格情報の種類をIssuedTokenに設定すると、このプロパティはClientFactoryに存在することになります(ヒント:IChannelには同等の "SetProperty"がありません。 )。

<binding name="OMGWTFLOL22" transferMode="Streamed" >
    <security mode="Message">
        <message clientCredentialType="IssuedToken"/>
    </security>
</binding>

甘い。これ以上のNREはありません。しかし、今では私のクライアントは出生時の障害(彼を愛している、カントー)。 WCF診断を掘り下げる(ヒント:最悪の敵を押しつぶし、あなたの前で追い払った後、女性と子供の嘆きを味わう直前に)、サーバーとクライアント間のセキュリティの不一致が原因だと思います。

要求されたアップグレードは「net.tcp:// localhost:49627/MyService」ではサポートされていません。これは、バインディングの不一致が原因である可能性があります(たとえば、サーバーではなくクライアントでセキュリティが有効になっている)。

ホストの診断をチェックします(再び:クラッシュ、ドライブ、ログの読み取り、嘆きを味わいます)、これは本当です

プロトコルタイプapplication/ssl-tlsは、そのタイプのアップグレードをサポートしないサービスに送信されました。

「まあ、自己」と私は言います、「ホストのメッセージセキュリティをオンにするだけです!」そして私は。 それがどのように見えるかを知りたい場合、それはクライアント設定の正確なコピーです。調べる。

結果:Kaboom.

バインディング( 'NetTcpBinding'、 ' http://tempuri.org/ ')は、メッセージレベルのセキュリティと共に構成できないストリーミングをサポートしています。別の転送モードを選択するか、トランスポートレベルセキュリティを選択することを検討してください。

したがって、 my Hostは、tokens を介してストリーミングおよび保護することはできません。キャッチ-22。

tl; dr:WIF ??? を使用して、ストリーミングされたnet.tcp WCFエンドポイントをセキュリティで保護する方法

180
Will

WCFには、ストリーミングに関するいくつかの領域で問題があります(私はあなたを見ています、MTOM1)事前認証の実行方法に根本的な問題があるため、ほとんどの人が機能するはずであると考える方法です(最初のリクエストではなく、そのチャネルの後続のリクエストにのみ影響します)。最後にあなたの所に行きます。通常、HTTPチャレンジは次のように機能します。

  1. クライアントが匿名でサーバーにヒットする
  2. サーバーは、申し訳ありませんが、401、私は認証が必要だと言います
  3. クライアントが認証トークンでサーバーにヒットする
  4. サーバーが受け入れます。

サーバー上のWCFエンドポイントでMTOMストリーミングを有効にしようとしても、文句は言われません。ただし、クライアントプロキシ上で構成すると(必要に応じて、バインディングと一致する必要があります)、激しい死で爆発します。この理由は、WCFが防止しようとしている上記の一連のイベントは次のとおりだからです。

  1. クライアントは1回のPOSTで匿名でサーバーに100MBファイルをストリーミングします
  2. サーバーは申し訳ありません、401、私は認証が必要だと言います
  3. クライアントは再び認証ヘッダー付きでサーバーに100MBのファイルをストリーミングします
  4. サーバーが受け入れます。

100MBを送信するだけでよいときに、サーバーに200MBを送信したことに注意してください。さて、これが問題です。答えは、最初の試行で認証を送信することですが、これはカスタム動作を記述しないとWCFでは不可能です。とにかく、私は脱線します。

あなたの問題

最初に、あなたがしようとしていることは不可能であることを教えてください2。さて、あなたが車輪の回転を止めるために、その理由を教えてください:

あなたが今、同様のクラスの問題にさまよいていることは、私に衝撃を与えます。メッセージレベルのセキュリティを有効にした場合、クライアントはws-securityで必要な通常のハッシュ関数とxml署名でメッセージを実際に閉じる前に、データストリーム全体をメモリにロードする必要があります。単一のメッセージ(実際にはメッセージではありませんが、単一の連続したストリームです)に署名するためにストリーム全体を読み取る必要がある場合は、ここで問題を確認できます。 WCFは、メッセージセキュリティを計算するために「ローカル」に一度ストリーミングする必要があり、その後、サーバーに送信するために再度ストリーミングする必要があります。これは明らかに愚かなことなので、WCFはストリーミングデータのメッセージレベルのセキュリティを許可しません。

したがって、ここでの簡単な答えは、最初のWebサービスへのパラメーターとして、またはSOAPヘッダーとしてトークンを送信し、カスタム動作を使用して検証することです。WSを使用することはできません。 -これを行うためのセキュリティ率直に言って、これは単なるWCFの問題ではありません-他のスタックで実際にどのように機能するかわかりません。

MTOM問題の解決

これは、基本認証のMTOMストリーミングの問題をどのように解決したかを示す例にすぎません。したがって、おそらくこれを理解し、問題に似たものを実装できます。重要な点は、カスタムメッセージインスペクターを有効にするには、トランスポートレベル(SSL)を除くクライアントプロキシのセキュリティの概念をすべて無効にする必要があることです(サーバーでは有効のままです)。

this._contentService.Endpoint.Behaviors.Add(
    new BasicAuthenticationBehavior(
        username: this.Settings.HttpUser,
        password: this.Settings.HttpPass));
var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding;
binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only            
binding.Security.Transport.ClientCredentialType = 
   HttpClientCredentialType.None; // Do not provide

ここでは、メッセージインスペクターとカスタム動作を使用して自分自身を提供するため、ここでトランスポートセキュリティをオフにしていることに注意してください。

internal class BasicAuthenticationBehavior : IEndpointBehavior
{
    private readonly string _username;
    private readonly string _password;

    public BasicAuthenticationBehavior(string username, string password)
    {
        this._username = username;
        this._password = password;
    }
    public void AddBindingParameters(ServiceEndpoint endpoint, 
        BindingParameterCollection bindingParameters) { }
    public void ApplyClientBehavior(ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
        var inspector = new BasicAuthenticationInspector(
            this._username, this._password);
        clientRuntime.MessageInspectors.Add(inspector);
    }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher) { }
    public void Validate(ServiceEndpoint endpoint) { }
}

internal class BasicAuthenticationInspector : IClientMessageInspector
{
    private readonly string _username;
    private readonly string _password;

    public BasicAuthenticationInspector(string username, string password)
    {
        this._username = username;
        this._password = password;
    }

    public void AfterReceiveReply(ref Message reply,
        object correlationState) { }

    public object BeforeSendRequest(ref Message request,
        IClientChannel channel)
    {
        // we add the headers manually rather than using credentials 
        // due to proxying issues, and with the 101-continue http verb 
        var authInfo = Convert.ToBase64String(
            Encoding.Default.GetBytes(this._username + ":" + this._password));

        var messageProperty = new HttpRequestMessageProperty();
        messageProperty.Headers.Add("Authorization", "Basic " + authInfo);
        request.Properties[HttpRequestMessageProperty.Name] = messageProperty;

        return null;
    }
}

したがって、この例は、MTOM問題に苦しんでいる人を対象としていますが、プライマリWIFで保護されたトークンサービスによって生成されたトークンを認証するためのスケルトンとしても同様です。

お役に立てれば。

(1) 大きなデータとストリーミング

(2) WCFのメッセージセキュリティ (「欠点」を参照)

41
x0n