Программирование безопасности WCF

В этом разделе описываются основные задачи программирования, используемые для создания безопасного приложения Windows Communication Foundation (WCF). В этом разделе рассматриваются только проверка подлинности, конфиденциальность и целостность, коллективно известные как безопасности передачи. Этот раздел не охватывает авторизацию (контроль доступа к ресурсам или службам); для получения сведений об авторизации см. раздел .

Примечание.

Ценное введение в концепции безопасности, особенно в отношении WCF, см. в наборе учебных пособий по шаблонам и практическим рекомендациям на MSDN в разделах сценарии, шаблоны и рекомендации по реализации для улучшения веб-служб (WSE) 3.0.

Программирование безопасности WCF основано на трех шагах, которые задаются следующим образом: режим безопасности, тип учетных данных клиента и значения учетных данных. Эти действия можно выполнить с помощью кода или конфигурации.

Настройка режима безопасности

Ниже описаны общие действия по программированию с использованием режима безопасности в WCF:

  1. Выберите одну из предопределенных привязок, соответствующих требованиям приложения. Список вариантов привязки см. в разделе System-Provided Привязки. По умолчанию каждая привязка включает безопасность. Одним из исключений является класс BasicHttpBinding (с помощью конфигурации <basicHttpBinding>).

    Выбранная привязка определяет транспорт. Например, WSHttpBinding использует HTTP в качестве транспорта; NetTcpBinding использует TCP.

  2. Выберите один из режимов безопасности привязки. Обратите внимание, что выбранная привязка определяет доступный режим. Например, WSDualHttpBinding не разрешает транспортную безопасность (это не предусмотрено). Аналогичным образом, ни MsmqIntegrationBinding, ни NetNamedPipeBinding не разрешает безопасность сообщений.

    Доступны три варианта.

    1. Transport

      Безопасность транспорта зависит от механизма использования выбранной привязки. Например, если вы используете WSHttpBinding, то механизм безопасности — secure Sockets Layer (SSL) (также механизм протокола HTTPS). Как правило, основное преимущество транспортной безопасности заключается в том, что она обеспечивает хорошую пропускную способность независимо от того, какой транспорт вы используете. Однако у него есть два ограничения: первый заключается в том, что механизм транспорта определяет тип учетных данных, используемый для проверки подлинности пользователя. Это недостаток только в том случае, если служба должна взаимодействовать с другими службами, требующими различных типов учетных данных. Во-вторых, поскольку безопасность не применяется на уровне сообщения, она реализуется поэтапно, а не от конца до конца. Это последнее ограничение является проблемой, только если путь сообщения между клиентом и службой включает посредников. Дополнительную информацию о том, какой транспорт использовать, см. в разделе "Выбор транспорта". Чтобы получить дополнительную информацию об обеспечении транспортной безопасности, см. Обзор безопасности транспорта.

    2. Message

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

      Дополнительные сведения см. в безопасности сообщений.

    3. TransportWithMessageCredential

      Этот выбор использует уровень транспорта для защиты передачи сообщений, в то время как каждое сообщение содержит богатые учетные данные, необходимые другим службам. Это объединяет преимущество безопасности транспорта с широкими учетными данными безопасности сообщений. Это доступно со следующими привязками: BasicHttpBinding, WSFederationHttpBinding, NetPeerTcpBindingи WSHttpBinding.

  3. Если вы решите использовать транспортную безопасность для HTTP (другими словами, HTTPS), необходимо также настроить хост с SSL-сертификатом и включить SSL на порту. Для получения дополнительной информации см. Безопасность транспортировки HTTP.

  4. Если вы используете WSHttpBinding и не требуется устанавливать безопасный сеанс, задайте для свойства EstablishSecurityContext значение false.

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

Настройка типа учетных данных клиента

Выберите тип учетных данных клиента соответствующим образом. Дополнительные сведения см. в разделе Выбор типа учетных данных. Доступны следующие типы учетных данных клиента:

  • Windows

  • Certificate

  • Digest

  • Basic

  • UserName

  • NTLM

  • IssuedToken

В зависимости от способа установки режима необходимо задать тип учетных данных. Например, если вы выбрали wsHttpBindingи установили режим "Сообщение", то вы можете также установить атрибут clientCredentialType элемента Message одним из следующих значений: None, Windows, UserName, Certificateи IssuedToken, как показано в следующем примере конфигурации.

<system.serviceModel>
<bindings>
  <wsHttpBinding>
    <binding name="myBinding">
      <security mode="Message"/>
      <message clientCredentialType="Windows"/>
    </binding>
  </wsHttpBinding>
</bindings>
</system.serviceModel>

Или в коде:

WSHttpBinding b = new WSHttpBinding();
b.Name = "myBinding";
b.Security.Mode = SecurityMode.Message;
b.Security.Message.ClientCredentialType=MessageCredentialType.Windows;
Dim b As New WSHttpBinding()
b.Name = "myBinding"
b.Security.Mode = SecurityMode.Message
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows

Настройка значений учетных данных службы

Выбрав тип учетных данных клиента, необходимо задать фактические учетные данные для службы и клиента. В службе учетные данные задаются с помощью класса ServiceCredentials и возвращаются свойством Credentials класса ServiceHostBase. Привязка, используемая, подразумевает тип учетных данных службы, выбранный режим безопасности и тип учетных данных клиента. Следующий код задает сертификат для учетных данных службы.

// Create the binding for an endpoint.
NetTcpBinding b = new NetTcpBinding();
b.Security.Mode = SecurityMode.Message;

// Create the ServiceHost for a calculator.
Uri baseUri = new Uri("net.tcp://MachineName/tcpBase");
Uri[] baseAddresses = new Uri[] { baseUri };
ServiceHost sh = new ServiceHost(typeof(Calculator), baseAddresses);

// Add an endpoint using the binding and a new address.
Type c = typeof(ICalculator);
sh.AddServiceEndpoint(c, b, "MyEndpoint");

// Set a certificate as the credential for the service.
sh.Credentials.ServiceCertificate.SetCertificate(
    StoreLocation.LocalMachine,
    StoreName.My,
    X509FindType.FindBySubjectName,
    "client.com");
try
{
    sh.Open();
    Console.WriteLine("Listening....");
    Console.ReadLine();
    sh.Close();
}
catch (CommunicationException ce)
{
    Console.WriteLine($"A communication error occurred: {ce.Message}");
    Console.WriteLine();
}
catch (System.Exception exc)
{
    Console.WriteLine($"An unforeseen error occurred: {exc.Message}");
    Console.ReadLine();
}
' Create the binding for an endpoint.
Dim b As New NetTcpBinding()
b.Security.Mode = SecurityMode.Message

' Create the ServiceHost for a calculator.
Dim baseUri As New Uri("net.tcp://MachineName/tcpBase")
Dim baseAddresses() As Uri = {baseUri}
Dim sh As New ServiceHost(GetType(Calculator), baseAddresses)

' Add an endpoint using the binding and a new address.
Dim c As Type = GetType(ICalculator)
sh.AddServiceEndpoint(c, b, "MyEndpoint")

' Set a certificate as the credential for the service.
sh.Credentials.ServiceCertificate.SetCertificate( _
                StoreLocation.LocalMachine, _
                StoreName.My, _
                X509FindType.FindBySubjectName, _
                "contoso.com")
Try
    sh.Open()
    Console.WriteLine("Listening....")
    Console.ReadLine()
    sh.Close()
Catch ce As CommunicationException
    Console.WriteLine("A communication error occurred: {0}", ce.Message)
    Console.WriteLine()
Catch exc As System.Exception
    Console.WriteLine("An unforeseen error occurred: {0}", exc.Message)
    Console.ReadLine()
End Try

Настройка значений учетных данных клиента

На клиенте задайте значения учетных данных клиента с помощью класса ClientCredentials, возвращаемых свойством ClientCredentials класса ClientBase<TChannel>. Следующий код задает сертификат в качестве учетных данных для клиента с помощью протокола TCP.

// Create a NetTcpBinding and set its security properties. The
// security mode is Message, and the client must be authenticated with
// Windows. Therefore the client must be on the same Windows domain.
NetTcpBinding b = new NetTcpBinding();
b.Security.Mode = SecurityMode.Message;
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows;

// Set a Type variable for use when constructing the endpoint.
Type c = typeof(ICalculator);

// Create a base address for the service.
Uri tcpBaseAddress =
    new Uri("net.tcp://machineName.Domain.Contoso.com:8036/serviceName");
// The base address is in an array of URI objects.
Uri[] baseAddresses = new Uri[] { tcpBaseAddress };
// Create the ServiceHost with type and base addresses.
ServiceHost sh = new ServiceHost(typeof(CalculatorClient), baseAddresses);

// Add an endpoint to the service using the service type and binding.
sh.AddServiceEndpoint(c, b, "");
sh.Open();
string address = sh.Description.Endpoints[0].ListenUri.AbsoluteUri;
Console.WriteLine($"Listening @ {address}");
Console.WriteLine("Press enter to close the service");
Console.ReadLine();
' Create a NetTcpBinding and set its security properties. The
' security mode is Message, and the client must be authenticated with
' Windows. Therefore the client must be on the same Windows domain.
Dim b As New NetTcpBinding()
b.Security.Mode = SecurityMode.Message
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows

' Set a Type variable for use when constructing the endpoint.
Dim c As Type = GetType(ICalculator)

' Create a base address for the service.
Dim tcpBaseAddress As New Uri("net.tcp://machineName.Domain.Contoso.com:8036/serviceName")
' The base address is in an array of URI objects.
Dim baseAddresses() As Uri = {tcpBaseAddress}
' Create the ServiceHost with type and base addresses.
Dim sh As New ServiceHost(GetType(CalculatorClient), baseAddresses)

' Add an endpoint to the service using the service type and binding.
sh.AddServiceEndpoint(c, b, "")
sh.Open()
Dim address As String = sh.Description.Endpoints(0).ListenUri.AbsoluteUri
Console.WriteLine("Listening @ {0}", address)
Console.WriteLine("Press enter to close the service")
Console.ReadLine()

См. также