キャッチ22により、ストリーミングされたTCP WCFサービスがWIFでセキュリティ保護されなくなり、クリスマスと精神衛生が台無しになる 質問する

キャッチ22により、ストリーミングされたTCP WCFサービスがWIFでセキュリティ保護されなくなり、クリスマスと精神衛生が台無しになる 質問する

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

これは不可能のようです。そして、もしその落とし穴を回避できなかったら、私のクリスマスは台無しになり、陽気な買い物客が私の冷えていく体を踏みつけながら、溝の中で酒を飲んで死んでしまうことになるでしょう。みんな、本気ですよ。

なぜこれが不可能なのでしょうか? これがジレンマです。

クライアントでは、汎用 XML セキュリティ トークントークン サーバーから取得します。問題ありません。

// 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 は、そのタイプのアップグレードをサポートしていないサービスに送信されました。

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

結果:カブーン。

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

それで、私のホストはトークン経由でストリーミングとセキュリティ保護の両方を行うことができません. キャッチ22。

tl;dr: WIF を使用してストリーミングされた net.tcp WCF エンドポイントを保護するにはどうすればよいですか?

ベストアンサー1

WCF には、ストリーミングに関するいくつかの領域で問題があります (MTOM 1がそうです)。これは、ほとんどの人が期待するとおりに事前認証を実行できないという根本的な問題が原因です (これは、そのチャネルの後続のリクエストにのみ影響し、最初のリクエストには影響しません)。わかりました。これはまさにあなたの問題ではありませんが、最後にあなたの問題について説明しますので、このまま読み進めてください。通常、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 は、メッセージ セキュリティを計算するためにそれを「ローカル」で 1 回ストリーミングし、次にそれを再度ストリーミングしてサーバーに送信する必要があります。これは明らかにばかげたことです。そのため、WCF はストリーミング データに対してメッセージ レベルのセキュリティを許可しません。

したがって、ここでの簡単な答えは、トークンを最初の Web サービスへのパラメータとして、または SOAP ヘッダーとして送信し、カスタム動作を使用して検証する必要があるということです。これを行うために WS-Security を使用することはできません。率直に言って、これは 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 におけるメッセージ セキュリティ(「デメリット」を参照)

おすすめ記事