Catch-22 предотвращает передачу потоковой службы TCP WCF через WIF; разрушая мое Рождество, психическое здоровье

181

У меня есть требование для защиты потоковой конечной точки службы WCF net.tcp с помощью WIF . Он должен аутентифицировать входящие звонки на наш токен-сервер. Служба потоковая, потому что она предназначена для передачи больших объемов данных и прочего.

Это кажется невозможным. И если я не смогу обойти добычу, мое Рождество испортится, и я буду пить до смерти в канаве, пока веселые покупатели переступают через мое медленно остывающее тело. Серьезные слова, ребята.

Почему это невозможно? Вот Поймай-22.

На клиенте мне нужно создать канал с GenericXmlSecurityToken, который я получаю от нашего токен-сервера. Без проблем.

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

Я сказал "нет проблем"? Problemo. На самом деле, NullReferenceExceptionстиль проблемный.

«Братан, - спросил я Фреймворк, - ты вообще проверил ноль?» Фреймворк молчал, поэтому я разобрал и обнаружил, что

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

был источником исключения, и что GetPropertyзвонок возвращался null. Итак, WTF? Оказывается, что если я включу безопасность сообщений и установлю тип учетных данных клиента, IssuedTokenто это свойство теперь существует в ClientFactory(protip: в 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/ ') поддерживает потоковую передачу, которую нельзя настроить вместе с безопасностью на уровне сообщений. Подумайте о выборе другого режима передачи или безопасности на транспортном уровне.

Таким образом, мой хост не может быть как потоковым, так и защищенным с помощью токенов . Словить 22.

tl; dr: Как я могу защитить потоковую конечную точку WCF net.tcp, используя WIF ???

BoltClock
источник
3
Хорошо, возможно, здесь есть неосведомленный вопрос, но действительно ли WIF требует режим сообщений? Транспортный режим звучит так, как будто он лучше работает с потоковой передачей, что-то вроде явно непроверенного<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
Йоахим Исакссон
3
TransportWithMessageCredentialРежим может быть другой вариант.
Иоахим Исакссон
3
TMLK, MessageSecurity может подписывать и шифровать буферизованную полезную нагрузку, но суетится при работе с потоками. Рассматривали ли вы использование authenticationMode = IssuedTokenOverTransport?
OnoSendai
7
Позвольте мне посмотреть, смогу ли я вызвать призраков прошлого, чтобы потом спасти ваш отпуск. Некоторые подсказки здесь: social.msdn.microsoft.com/Forums/vstudio/en-US/…
OnoSendai
2
Есть ли шанс, что вы сможете опубликовать тестовый проект, с которым могли бы поэкспериментировать другие?
антидух

Ответы:

41

В WCF есть несколько проблем с потоковой передачей (я смотрю на вас, MTOM 1 ) из-за фундаментальной проблемы того, как он не может выполнить предварительную аутентификацию, как большинство людей считает, что это должно работать (это влияет только на последующие запросы для этого канала , не первый запрос) Хорошо, так что это не совсем ваша проблема, но, пожалуйста, следуйте, как я доберусь до вашего в конце. Обычно HTTP-вызов работает так:

  1. клиент обращается к серверу анонимно
  2. сервер говорит, извините, 401, мне нужна аутентификация
  3. клиент обращается к серверу с токеном аутентификации
  4. Сервер принимает.

Теперь, если вы когда-нибудь попытаетесь включить потоковую передачу MTOM на конечной точке WCF на сервере, он не будет жаловаться. Но, когда вы настраиваете его на клиентском прокси-сервере (как и должно быть, они должны соответствовать привязкам), он взорвется пламенной смертью. Причина этого заключается в том, что приведенная выше последовательность событий, которую WCF пытается предотвратить, заключается в следующем:

  1. клиент передает анонимно файл размером 100 МБ на сервер в одном POST
  2. сервер говорит, извините, 401, мне нужна аутентификация
  3. клиент снова передает 100MB файл на сервер с заголовком аутентификации
  4. Сервер принимает.

Обратите внимание, что вы только что отправили 200 МБ на сервер, когда вам нужно было только отправить 100 МБ. Ну, это проблема. Ответ заключается в отправке аутентификации с первой попытки, но это невозможно в WCF без написания пользовательского поведения. Во всяком случае, я отвлекся.

Твоя проблема

Прежде всего, позвольте мне сказать вам, что то, что вы пытаетесь, невозможно 2 . Теперь, чтобы вы перестали вращать свои колеса, позвольте мне сказать вам, почему:

Меня поражает, что вы сейчас блуждаете в подобном классе проблем. Если вы включите защиту на уровне сообщений, клиент должен загрузить весь поток данных в память, прежде чем он сможет фактически закрыть сообщение с помощью обычной хеш-функции и XML-подписи, требуемой ws-security. Если он должен прочитать весь поток, чтобы подписать одно сообщение (которое на самом деле не является сообщением, но это один непрерывный поток), тогда вы можете увидеть проблему здесь. WCF должен будет один раз передать его «локально», чтобы вычислить безопасность сообщения, а затем снова передать его, чтобы отправить на сервер. Это явно глупо, поэтому WCF не разрешает безопасность на уровне сообщений для потоковой передачи данных.

Итак, простой ответ здесь заключается в том, что вы должны отправить токен либо в качестве параметра в исходный веб-сервис, либо в виде заголовка 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 (см. «Недостатки».)

x0n
источник
MTOM and Basic Authorizationа MTOM и OAuth2 ?
Kiquenet