Catch-22 zapobiega przesyłaniu strumieniowej usługi TCP WCF przez WIF; rujnuje moje święta, zdrowie psychiczne


181

Mam wymaganie zabezpieczenia punktu końcowego usługi net.tcp usługi WCF za pomocą WIF . Powinien on uwierzytelniać połączenia przychodzące przeciwko naszemu serwerowi tokenów. Usługa jest przesyłana strumieniowo, ponieważ jest przeznaczona do przesyłania dużych ilości danych i innych rzeczy.

To wydaje się niemożliwe. A jeśli nie uda mi się obejść haczyka, moje Boże Narodzenie zostanie zrujnowane i wypiję się na śmierć w rynsztoku, podczas gdy wesoło kupujący przechodzą nad moim powoli ochładzającym się ciałem. Poważnie, chłopaki.

Dlaczego to jest niemożliwe? Oto Catch-22.

Na kliencie muszę utworzyć kanał z GenericXmlSecurityToken, który otrzymuję z naszego serwera tokenów. 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 „bez problemu”? Problemo W rzeczywistości NullReferenceExceptionproblem stylu.

„Bro”, zapytałem Framework, „czy w ogóle sprawdzasz zerowo?” Framework był cichy, więc zdemontowałem i znalazłem to

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

było źródłem wyjątku i że GetPropertypołączenie było zwracane null. Więc WTF? Okazuje się, że jeśli włączę Zabezpieczenia wiadomości i ustawię typ poświadczenia klienta na, IssuedTokenwówczas 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. Nigdy więcej NRE. Jednak teraz mój klient ma wadę od urodzenia (nadal go kocham, tho). Przekopując się przez diagnostykę WCF (protip: zmuś swoich najgorszych wrogów do zrobienia tego po zmiażdżeniu ich i poprowadzeniu ich przed tobą, ale tuż przed czerpaniem przyjemności z rozpaczy ich kobiet i dzieci), widzę, że jest to spowodowane niedopasowaniem bezpieczeństwa między serwerem a klientem.

Żądana aktualizacja nie jest obsługiwana przez „net.tcp: // localhost: 49627 / MyService”. Przyczyną może być niedopasowane powiązania (na przykład zabezpieczenia włączone na kliencie, a nie na serwerze).

Sprawdzając diagi gospodarza (ponownie: zmiażdż, jeźdź, 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 ja tak. Jeśli chcesz wiedzieć, jak to wygląda, jest to dokładna kopia konfiguracji klienta. Sprawdzać.

Wynik: Kaboom.

Powiązanie („NetTcpBinding”, „ http://tempuri.org/ ”) obsługuje przesyłanie strumieniowe, którego nie można skonfigurować razem z zabezpieczeniami na poziomie wiadomości. Zastanów się nad wyborem innego trybu przesyłania lub zabezpieczeniem na poziomie transportu.

Tak więc mój host nie może być przesyłany strumieniowo i zabezpieczany za pomocą tokenów . Złap 22.

tl; dr: Jak zabezpieczyć strumieniowany punkt końcowy WCF net.tcp za pomocą WIF ???


3
Ok, prawdopodobnie ignoranckie pytanie tutaj, ale czy WIF naprawdę wymaga trybu wiadomości? Wygląda na to, że tryb transportu lepiej działałby ze strumieniowaniem, coś jak oczywiście nie przetestowany<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
Joachim Isaksson,

3
TransportWithMessageCredentialtryb może być inną opcją.
Joachim Isaksson,

3
TMLK, MessageSecurity może podpisywać i szyfrować buforowany ładunek, ale gubi się przy przetwarzaniu strumieni. Czy zastanawiałeś się nad użyciem authenticationMode = IssuedTokenOverTransport?
OnoSendai,

7
Pokażę, czy mogę wezwać duchy z przeszłości, aby pomóc w ocaleniu twoich wakacji. Kilka wskazówek tutaj: social.msdn.microsoft.com/Forums/vstudio/en-US/…
OnoSendai,

2
Czy jest szansa na opublikowanie projektu testowego, z którym inni mogliby eksperymentować?
antiduh

Odpowiedzi:


41

WCF ma problemy z transmisją strumieniową w kilku obszarach (patrzę na ciebie, MTOM 1 ) ze względu na podstawowy problem polegający na tym, że nie wykonuje wstępnego uwierzytelnienia w sposób, w jaki większość ludzi uważa, że ​​powinno to działać (wpływa tylko na kolejne żądania dla tego kanału , nie pierwsza prośba) Ok, więc to nie jest dokładnie twój problem, ale proszę postępuj zgodnie z instrukcją, bo na końcu do ciebie dotrę. Zwykle wyzwanie HTTP działa w następujący sposób:

  1. klient uderza serwer anonimowo
  2. serwer mówi, przepraszam, 401, Potrzebuję uwierzytelnienia
  3. klient trafia na serwer za pomocą tokena uwierzytelniającego
  4. serwer akceptuje.

Teraz, jeśli kiedykolwiek spróbujesz włączyć strumieniowanie MTOM na punkcie końcowym WCF na serwerze, nie będzie narzekać. Ale jeśli skonfigurujesz go na serwerze proxy klienta (tak jak powinieneś, muszą one pasować do powiązań), wybuchnie to ognistą śmiercią. Powodem tego jest powyższa sekwencja zdarzeń, której WCF próbuje zapobiec:

  1. klient przesyła anonimowo 100 MB pliku na serwer w jednym POST
  2. serwer mówi przepraszam, 401, Potrzebuję uwierzytelnienia
  3. klient ponownie przesyła strumieniowo plik 100 MB na serwer z nagłówkiem uwierzytelniania
  4. serwer akceptuje.

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

Twój problem

Po pierwsze, powiem ci, że to, czego próbujesz, jest niemożliwe 2 . Teraz, abyście przestali kręcić kołami, powiem wam, dlaczego:

Uderza mnie, że wędrujesz teraz w podobnej klasie problemów. Jeśli włączysz zabezpieczenia 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 wymaganego przez ws-security. Jeśli musi odczytać 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ł przesłać go raz „lokalnie”, aby obliczyć bezpieczeństwo wiadomości, a następnie przesłać ją ponownie, aby wysłać ją na serwer. Jest to oczywiście głupia sprawa, więc WCF nie zezwala na bezpieczeństwo na poziomie komunikatów dla przesyłania strumieniowego danych.

Tak więc prosta odpowiedź tutaj jest taka, że ​​powinieneś wysłać token albo jako parametr do początkowej usługi internetowej, albo jako nagłówek SOAP i użyć niestandardowego zachowania, aby to sprawdzić. 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 praktycznie działać dla innych stosów.

Rozwiązywanie problemu MTOM

To tylko przykład, w jaki sposób rozwiązałem problem z transmisją MTOM dla podstawowego uwierzytelnienia, więc być może mógłbyś spróbować i wdrożyć coś podobnego do swojego problemu. Chodzi o to, że aby włączyć niestandardowego inspektora wiadomości, musisz wyłączyć wszelkie pojęcia bezpieczeństwa na klienckim serwerze proxy (pozostaje ono włączone na serwerze) oprócz 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

Pamiętaj, że wyłączyłem tutaj zabezpieczenia transportu, ponieważ zapewniam, że korzystam z 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 na problem MTOM, ale także jako szkielet do zaimplementowania czegoś podobnego do uwierzytelnienia tokena wygenerowanego przez podstawową usługę tokenu zabezpieczoną przez WIF.

Mam nadzieję że to pomoże.

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

(2) Bezpieczeństwo wiadomości w WCF (patrz „wady”)


MTOM and Basic Authorizationoraz MTOM i OAuth2 ?
Kiquenet
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.