Catch-22 verhindert, dass gestreamte TCP-WCF-Dienste durch WIF gesichert werden können. mein Weihnachten ruinieren, geistige Gesundheit


181

Ich muss einen gestreamten WCF net.tcp-Dienstendpunkt mit WIF sichern . Es sollte eingehende Anrufe gegen unseren Tokenserver authentifizieren. Der Dienst wird gestreamt, da er für die Übertragung großer Datenmengen ausgelegt ist.

Dies scheint unmöglich zu sein. Und wenn ich den Haken nicht umgehen kann, wird mein Weihnachtsfest ruiniert und ich werde mich in einer Gosse zu Tode trinken, während fröhliche Käufer über meinen langsam abkühlenden Körper treten. Totes ernst, Leute.

Warum ist das unmöglich? Hier ist der Catch-22.

Auf dem Client muss ich einen Kanal mit dem GenericXmlSecurityToken erstellen, das ich von unserem Tokenserver erhalte. Kein Problem.

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

Habe ich "no problemo" gesagt? Problemo. In der Tat NullReferenceExceptionStil problemo.

"Bro", fragte ich das Framework, "prüfst du überhaupt nicht?" Das Framework war still, also habe ich es zerlegt und festgestellt

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

war die Quelle der Ausnahme und dass der GetPropertyAnruf zurückkehrte null. Also, WTF? Es stellt sich heraus, dass, wenn ich die Nachrichtensicherheit aktiviere und den Clientanmeldeinformationstyp auf setze, IssuedTokendiese Eigenschaft jetzt im ClientFactory(protip: Es gibt kein "SetProperty" -Äquivalent in IChannel, dem Bastard) vorhanden ist.

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

Süss. Keine NREs mehr. Jetzt ist mein Klient jedoch bei der Geburt fehlerhaft (liebe ihn immer noch, tho). Wenn ich die WCF-Diagnose durchforste (Protip: Lassen Sie Ihre schlimmsten Feinde dies tun, nachdem Sie sie niedergeschlagen und vor Ihnen gefahren haben, aber kurz bevor Sie die Wehklagen ihrer Frauen und Kinder genießen), sehe ich, dass dies auf eine Sicherheitslücke zwischen Server und Client zurückzuführen ist.

Das angeforderte Upgrade wird von 'net.tcp: // localhost: 49627 / MyService' nicht unterstützt. Dies kann auf nicht übereinstimmende Bindungen zurückzuführen sein (z. B. auf dem Client und nicht auf dem Server aktivierte Sicherheit).

Wenn ich die Diags des Hosts überprüfe (noch einmal: zerquetschen, fahren, Protokolle lesen, Wehklagen genießen), sehe ich, dass dies wahr ist

Die Protokolltypanwendung / ssl-tls wurde an einen Dienst gesendet, der diese Art der Aktualisierung nicht unterstützt.

"Nun, ich selbst", sagt ich, "ich schalte einfach die Nachrichtensicherheit auf dem Host ein!" Und ich mache. Wenn Sie wissen möchten, wie es aussieht, handelt es sich um eine exakte Kopie der Client-Konfiguration. Sieh nach oben.

Ergebnis: Kaboom.

Die Bindung ('NetTcpBinding', ' http://tempuri.org/ ') unterstützt Streaming, das nicht zusammen mit der Sicherheit auf Nachrichtenebene konfiguriert werden kann. Wählen Sie einen anderen Übertragungsmodus oder die Sicherheit auf Transportebene.

So kann mein Gastgeber nicht beide gestreamten und gesichert über Token sein . Fang-22.

tl; dr: Wie kann ich einen gestreamten net.tcp WCF-Endpunkt mit WIF sichern?


3
Ok, wahrscheinlich unwissende Frage hier, aber benötigt WIF wirklich den Nachrichtenmodus? Transport-Modus klingt wie es besser mit Streaming funktionieren würde, so etwas wie der offensichtlich ungetestete<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
Joachim Isaksson

3
TransportWithMessageCredentialModus kann eine andere Option sein.
Joachim Isaksson

3
TMLK, MessageSecurity kann gepufferte Nutzdaten signieren und verschlüsseln, fummelt jedoch beim Umgang mit Streams. Haben Sie überlegt, authenticationMode = IssuedTokenOverTransport zu verwenden?
OnoSendai

7
Lassen Sie mich sehen, ob ich einige Geister aus der Vergangenheit beschwören kann, um Ihre Ferien zu retten. Einige Hinweise hier: social.msdn.microsoft.com/Forums/vstudio/en-US/…
OnoSendai

2
Gibt es eine Chance, ein Testfallprojekt zu veröffentlichen, mit dem andere experimentieren könnten?
Antiduh

Antworten:


41

WCF hat in einigen Bereichen Probleme mit Streaming (ich sehe Sie an, MTOM 1 ), da es ein grundlegendes Problem gibt, wie die Vorauthentifizierung nicht so durchgeführt wird, wie die meisten Leute denken, dass dies funktionieren sollte (dies betrifft nur nachfolgende Anforderungen für diesen Kanal , nicht die erste Anfrage) Ok, das ist also nicht genau Ihr Problem, aber bitte folgen Sie mir, da ich am Ende zu Ihnen komme. Normalerweise funktioniert die HTTP-Challenge folgendermaßen:

  1. Client trifft Server anonym
  2. Server sagt, sorry, 401, ich brauche Authentifizierung
  3. Client trifft Server mit Authentifizierungstoken
  4. Server akzeptiert.

Wenn Sie jemals versuchen, das MTOM-Streaming auf einem WCF-Endpunkt auf dem Server zu aktivieren, wird dies nicht beanstandet. Wenn Sie es jedoch auf dem Client-Proxy konfigurieren (wie Sie sollten, müssen sie mit den Bindungen übereinstimmen), explodiert es in einem feurigen Tod. Der Grund dafür ist, dass die obige Abfolge von Ereignissen, die WCF zu verhindern versucht, folgende ist:

  1. Der Client überträgt 100 MB-Dateien anonym in einem einzigen POST an den Server
  2. Server sagt Entschuldigung, 401, ich brauche Authentifizierung
  3. Der Client überträgt erneut 100 MB-Dateien mit einem Authentifizierungsheader an den Server
  4. Server akzeptiert.

Beachten Sie, dass Sie gerade 200 MB an den Server gesendet haben, als Sie nur 100 MB senden mussten. Nun, das ist das Problem. Die Antwort besteht darin, die Authentifizierung beim ersten Versuch zu senden. Dies ist jedoch in WCF nicht möglich, ohne ein benutzerdefiniertes Verhalten zu schreiben. Wie auch immer, ich schweife ab.

Ihr Problem

Lassen Sie mich zunächst sagen, dass das, was Sie versuchen, unmöglich ist 2 . Damit Sie nicht mehr durchdrehen, lassen Sie mich Ihnen sagen, warum:

Es fällt mir auf, dass Sie jetzt in einer ähnlichen Klasse von Problemen wandern. Wenn Sie die Sicherheit auf Nachrichtenebene aktivieren, muss der Client den gesamten Datenstrom in den Speicher laden, bevor er die Nachricht mit der für ws-security erforderlichen Hash-Funktion und XML-Signatur schließen kann. Wenn es den gesamten Stream lesen muss, um die einzelne Nachricht zu signieren (was nicht wirklich eine Nachricht ist, sondern ein einzelner kontinuierlicher Stream), können Sie das Problem hier sehen. WCF muss es einmal "lokal" streamen, um die Nachrichtensicherheit zu berechnen, und es dann erneut streamen, um es an den Server zu senden. Dies ist eindeutig eine dumme Sache, daher erlaubt WCF keine Sicherheit auf Nachrichtenebene für das Streaming von Daten.

Die einfache Antwort hier lautet also, dass Sie das Token entweder als Parameter an den ursprünglichen Webdienst oder als SOAP-Header senden und ein benutzerdefiniertes Verhalten verwenden sollten, um es zu validieren. Sie können WS-Security nicht verwenden, um dies zu tun. Ehrlich gesagt ist dies nicht nur ein WCF-Problem - ich kann nicht sehen, wie es praktisch für andere Stapel funktionieren könnte.

Lösen des MTOM-Problems

Dies ist nur ein Beispiel dafür, wie ich mein MTOM-Streaming-Problem für die Basisauthentifizierung gelöst habe. Vielleicht können Sie sich also den Mut machen und etwas Ähnliches für Ihr Problem implementieren. Der springende Punkt dabei ist, dass Sie zum Aktivieren Ihres benutzerdefinierten Nachrichteninspektors alle Sicherheitsbegriffe auf dem Client-Proxy (auf dem Server aktiviert) deaktivieren müssen, mit Ausnahme der Transportebene (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

Beachten Sie, dass ich die Transportsicherheit hier deaktiviert habe, da ich dies selbst mithilfe eines Nachrichteninspektors und benutzerdefinierten Verhaltens bereitstellen werde:

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;
    }
}

Dieses Beispiel ist also für alle gedacht, die unter dem MTOM-Problem leiden, aber auch als Grundgerüst für Sie, um etwas Ähnliches zu implementieren, um Ihr vom primären WIF-gesicherten Token-Dienst generiertes Token zu authentifizieren.

Hoffe das hilft.

(1) Große Datenmenge und Streaming

(2) Nachrichtensicherheit in WCF (siehe "Nachteile")


MTOM and Basic Authorizationund MTOM und OAuth2 ?
Kiquenet
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.