Pytanie Catch-22 uniemożliwia strumieniową usługę TCP WCF zabezpieczoną przez WIF; rujnując moje święta, zdrowie psychiczne


Mam obowiązek zabezpiecz strumieniowany punkt końcowy usługi net.tcp przy użyciu WIF. Powinien uwierzytelnić połączenia przychodzące na nasz serwer tokenów. Usługa jest przesyłana strumieniowo, ponieważ jest przeznaczona do przesyłania dużych ilości danych.

Wydaje się to niemożliwe.  A jeśli nie uda mi się ominąć tego haczyka, moje święta zostaną zrujnowane, a ja wypiję się na śmierć w rynsztoku, a wesołe kupujący przekroczą moje powoli chłodzące ciało. Dużo ważniaki, chłopaki.

Dlaczego to niemożliwe? Oto Catch-22.

Na kliencie muszę utworzyć kanał z GenericXmlSecurityToken Dostaję z naszego serwera tokenu. Bez problemu.

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

Czy powiedziałem "nie problemo"? Problemo. W rzeczywistości, NullReferenceException styl problemo.

"Bro", zapytałem Framework, "czy nawet zerujesz sprawdzić?" Framework milczał, więc rozłożyłem go i odkryłem

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

był źródłem wyjątku, i że GetProperty połączenie wróciło null. Więc WTF? Okazuje się, że jeśli włączę zabezpieczenie wiadomości i ustawię typ poświadczeń klienta na IssuedToken to ta właściwość istnieje teraz w ClientFactory (protip: Nie ma odpowiednika "SetProperty" w IChannel, draniu).

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

Słodkie. Koniec z NRE. Jednak teraz jest mój klient zerwany przy narodzinach (nadal go kocham, tho). Kopanie przez diagnostykę WCF (protip: spraw, aby twoi najgorsi wrogowie robili to po zmiażdżeniu ich i doprowadzeniu ich przed tobą, ale tuż przed cieszeniem się lamentowaniem ich kobiet i dzieci), widzę, że dzieje się tak z powodu niedopasowania bezpieczeństwa między serwerem a klientem.

Żądana aktualizacja nie jest obsługiwana przez "net.tcp: // localhost: 49627 / MyService". Może to wynikać z niezgodności wiązań (na przykład zabezpieczenia włączone na kliencie, a nie na serwerze).

Sprawdzam diagy hosta (ponownie: miażdż, prowadź, czytaj dzienniki, ciesz się lamentami), widzę, że to prawda

Typ protokołu application / ssl-tls został wysłany do usługi, która nie obsługuje tego typu aktualizacji.

"Cóż, ja", mówię, "po prostu włączę ochronę wiadomości na hoście!" I robię. Jeśli chcesz wiedzieć, jak to wygląda, jest to dokładna kopia konfiguracji klienta. Sprawdzać.

Wynik: Kaboom.

Wiązanie ("NetTcpBinding", "http://tempuri.org/') obsługuje strumieniowanie, którego nie można skonfigurować razem z zabezpieczeniem poziomu wiadomości. Zastanów się nad wyborem innego trybu przesyłania lub wybrania zabezpieczenia poziomu transportu.

Więc, mój host nie może być zarówno przesyłany strumieniowo, jak i zabezpieczony za pomocą tokenów. Złap 22.

tl; dr: Jak mogę zabezpieczyć strumień końcowy punktu końcowego net.tcp WCF za pomocą WIF ???


180
2017-12-19 20:54


pochodzenie


OK, prawdopodobnie niewiedzące pytanie tutaj, ale czy WIF naprawdę wymaga trybu wiadomości? Tryb transportu brzmi, jakby lepiej działał z transmisją strumieniową, coś takiego jak oczywiście niesprawdzony <security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security> - Joachim Isaksson
TransportWithMessageCredential tryb może być inną opcją. - Joachim Isaksson
TMLK, MessageSecurity może podpisać i zaszyfrować buforowany ładunek, ale zbija z tropu, gdy mamy do czynienia ze strumieniami. Czy rozważałeś użycie authenticationMode = IssuedTokenOverTransport? - OnoSendai
Pozwól mi zobaczyć, czy mogę przywołać niektóre duchy z przeszłości, aby pomóc uratować twoje wakacje. Kilka wskazówek tutaj: social.msdn.microsoft.com/Forums/vstudio/en-US/... - OnoSendai
Czy masz szansę opublikować projekt testowy, z którego inni mogą eksperymentować? - antiduh


Odpowiedzi:


WCF ma gotchas w kilku obszarach z streamingiem (patrzę na ciebie, MTOM1) ze względu na zasadniczy problem z niezapewnianiem wstępnego uwierzytelniania w taki sposób, w jaki większość ludzi uważa, że ​​powinna działać (wpływa to tylko na późniejsze żądania tego kanału, a nie na pierwsze żądanie) Ok, więc to nie jest dokładnie twój problem, ale postępuj zgodnie ze wskazówkami jak dotrę do ciebie na końcu. Normalnie wyzwanie HTTP działa w następujący sposób:

  1. klient przeszukuje serwer anonimowo
  2. serwer mówi, przepraszam, 401, potrzebuję uwierzytelnienia
  3. klient trafia serwer z tokenem uwierzytelniania
  4. serwer akceptuje.

Teraz, jeśli kiedykolwiek spróbujesz włączyć streaming MTOM na punkcie końcowym WCF na serwerze, nie będzie narzekać. Ale kiedy konfigurujesz go na serwerze proxy klienta (tak jak powinieneś, muszą pasować do wiązań), to eksploduje w ognistej śmierci. Powodem tego jest to, że powyższa sekwencja zdarzeń, które WCF próbuje zapobiec, jest następująca:

  1. klient przesyła do serwera plik 100 MB anonimowo w jednym pliku POST
  2. serwer przeprasza, 401, potrzebuję uwierzytelnienia
  3. klient ponownie przesyła plik 100 MB do serwera z nagłówkiem uwierzytelniania
  4. serwer akceptuje.

Zauważ, że właśnie wysłałeś 200MB na serwer, gdy potrzebujesz tylko wysłać 100MB. Cóż, to jest problem. Odpowiedzią jest wysłanie uwierzytelnienia przy pierwszej próbie, ale nie jest to możliwe w WCF bez pisania niestandardowego zachowania. W każdym razie, dygoczę.

Twój problem

Najpierw pozwól, że powiem ci, że to, co próbujesz, jest niemożliwe2. Teraz, abyś przestał obracać koła, pozwól mi powiedzieć, dlaczego:

Uderza mnie, że teraz wędrujesz w podobnej klasie problemu. Jeśli włączysz zabezpieczenie na poziomie komunikatu, klient musi załadować cały strumień danych do pamięci, zanim będzie mógł faktycznie zamknąć komunikat przy użyciu zwykłej funkcji skrótu i ​​podpisu XML wymaganych przez ws-security. Jeśli musisz przeczytać cały strumień, aby podpisać pojedynczą wiadomość (która tak naprawdę nie jest wiadomością, ale jest to pojedynczy ciągły strumień), możesz zobaczyć problem tutaj. WCF będzie musiał go przesłać strumieniowo raz "lokalnie", aby obliczyć zabezpieczenia wiadomości, a następnie przesłać strumień ponownie, aby wysłać go na serwer. Jest to wyraźnie głupia rzecz, więc WCF nie zezwala na poziom zabezpieczenia danych strumieniowych.

Tak więc prostą odpowiedzią jest to, że należy wysłać token jako parametr do początkowej usługi sieciowej lub jako nagłówek SOAP i użyć niestandardowego zachowania do sprawdzenia poprawności. W tym celu nie można użyć WS-Security. Szczerze mówiąc, nie jest to tylko kwestia WCF - nie widzę, jak mogłaby ona praktycznie działać w przypadku innych stosów.

Rozwiązywanie problemu MTOM

Jest to tylko przykład na to, jak rozwiązałem problem z strumieniowaniem MTOM w celu podstawowego uwierzytelnienia, więc być może mógłbyś wziąć na to odwagę i zaimplementować coś podobnego do twojego problemu. Sednem tego jest to, że aby włączyć niestandardowego kontrolera wiadomości, należy wyłączyć całe pojęcie bezpieczeństwa na serwerze proxy klienta (pozostaje on włączony na serwerze), niezależnie od poziomu transportu (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

Zwróć uwagę, że wyłączyłem zabezpieczenia transportu tutaj, ponieważ będę je udostępniał za pomocą inspektora wiadomości i niestandardowego zachowania:

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

Tak więc ten przykład jest dla każdego, kto cierpi z powodu problemu MTOM, ale także jako szkielet, aby zaimplementować coś podobnego do uwierzytelnienia tokenu generowanego przez podstawową usługę zabezpieczonego tokena WIF.

Mam nadzieję że to pomoże.

(1) Duże dane i przesyłanie strumieniowe

(2) Message Security w WCF (patrz "Wady.")


41
2018-01-05 17:12



MTOM and Basic Authorization, i MTOM i OAuth2 ? - Kiquenet