Programação de segurança do WCF
Este tópico descreve as tarefas de programação fundamentais usadas para criar um aplicativo seguro do WCF (Windows Communication Foundation). Este tópico abrange apenas autenticação, confidencialidade e integridade, conhecidas coletivamente como segurança de transferência. Este tópico não aborda autorização (o controle de acesso a recursos ou serviços); para obter informações sobre autorização, consulte Autorização.
Observação
Para obter uma introdução valiosa aos conceitos de segurança, especialmente em relação ao WCF, consulte o conjunto de padrões e tutoriais de práticas no MSDN em Cenários, padrões e diretrizes de implementação para aprimoramentos de serviços da Web (WSE) 3.0.
A programação da segurança do WCF baseia-se em três etapas que definem o seguinte: o modo de segurança, um tipo de credencial de cliente e os valores de credencial. Você pode executar essas etapas por meio de código ou configuração.
Configurando o modo de segurança
O seguinte explica as etapas gerais para programação com o modo de segurança no WCF:
Selecione uma das associações predefinidas apropriadas para seus requisitos de aplicativo. Para obter uma lista das opções de associação, consulte Associações fornecidas pelo sistema. Por padrão, quase todas as associações têm a segurança habilitada. A única exceção é a classe BasicHttpBinding (usando configuração, a <basicHttpBinding>).
A associação selecionada determina o transporte. Por exemplo, WSHttpBinding usa HTTP como transporte; NetTcpBinding usa TCP.
Selecione um dos modos de segurança para a associação. Observe que a associação selecionada determina as opções de modo disponíveis. Por exemplo, WSDualHttpBinding não permite segurança do transporte (não é uma opção). Da mesma forma, nem MsmqIntegrationBinding, nem NetNamedPipeBinding permitem segurança da mensagem.
Você tem três opções:
Transport
A segurança do transporte depende do mecanismo que a associação que você selecionou usa. Por exemplo, se você estiver usando
WSHttpBinding
, o mecanismo de segurança será Secure Sockets Layer - protocolo SSL (também o mecanismo para o protocolo HTTPS). De um modo geral, a principal vantagem da segurança do transporte é que ela oferece uma boa taxa de transferência, não importa qual transporte você esteja usando. No entanto, ele tem duas limitações: a primeira é que o mecanismo de transporte dita o tipo de credencial usado para autenticar um usuário. Isso é uma desvantagem somente se um serviço precisar interoperar com outros serviços que exigem diferentes tipos de credenciais. A segunda é que, como a segurança não é aplicada no nível da mensagem, ela é implementada salto a salto, em vez de ponta a ponta. Essa última limitação será um problema somente se o caminho da mensagem entre o cliente e o serviço incluir intermediários. Para obter mais informações sobre qual transporte usar, consulte Como escolher um transporte. Para obter mais informações sobre como usar a segurança do transporte, consulte Security Overview - Visão Geral de Segurança do transporte.Message
A segurança da mensagem significa que cada mensagem inclui os cabeçalhos e dados necessários para mantê-la segura. Como a composição dos cabeçalhos varia, você pode incluir qualquer número de credenciais. Isso se torna um fator se você estiver interoperando com outros serviços que exigem um tipo de credencial específico que um mecanismo de transporte não pode fornecer ou se a mensagem precisar ser usada com mais de um serviço, em que cada serviço exige um tipo de credencial diferente.
Para obter mais informações, consulte Segurança da mensagem.
TransportWithMessageCredential
Essa opção usa a camada de transporte para proteger a transferência de mensagens, enquanto cada mensagem inclui as credenciais avançadas de que outros serviços precisam. Isso combina a vantagem de desempenho da segurança de transporte com a vantagem de credenciais avançadas da segurança da mensagem. Isso está disponível com as seguintes associações: BasicHttpBinding, WSFederationHttpBinding, NetPeerTcpBinding e WSHttpBinding.
Se decidir usar segurança do transporte para HTTP (em outras palavras, HTTPS), você também deverá configurar o host com um certificado SSL e habilitar SSL em uma porta. Para obter mais informações, confira Segurança de transporte de HTTP.
Se você estiver usando WSHttpBinding e não precisar estabelecer uma sessão segura, defina a propriedade EstablishSecurityContext como
false
.Uma sessão segura ocorre quando um cliente e um serviço criam um canal usando uma chave simétrica (tanto o cliente quanto o servidor usam a mesma chave para a duração de uma conversa, até que a caixa de diálogo seja fechada).
Definindo o tipo de credencial de cliente
Selecione um tipo de credencial de cliente conforme apropriado. Para obter mais informações, consulte Como selecionar um tipo de credencial. Os seguintes tipos de credenciais de cliente estão disponíveis:
Windows
Certificate
Digest
Basic
UserName
NTLM
IssuedToken
Dependendo de como você define o modo, você deve definir o tipo de credencial. Por exemplo, se você selecionou wsHttpBinding
e definiu o modo como "Mensagem", também poderá definir o atributo clientCredentialType
do elemento Mensagem para um dos seguintes valores: None
, Windows
, UserName
, Certificate
e IssuedToken
, conforme mostrado no exemplo de configuração a seguir.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="myBinding">
<security mode="Message"/>
<message clientCredentialType="Windows"/>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Ou no código:
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
Como definir valores de credencial de serviço
Depois de selecionar um tipo de credencial de cliente, você deve definir as credenciais reais a serem usadas para o serviço e o cliente. No serviço, as credenciais são definidas usando a classe ServiceCredentials e retornadas pela propriedade Credentials da classe ServiceHostBase. A vinculação em uso implica o tipo de credencial de serviço, o modo de segurança escolhido e o tipo de credencial do cliente. O código a seguir define um certificado para uma credencial de serviço.
// 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: {0}", ce.Message);
Console.WriteLine();
}
catch (System.Exception exc)
{
Console.WriteLine("An unforeseen error occurred: {0}", 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
Definindo valores de credencial do cliente
No cliente, defina os valores de credencial do cliente usando a classe ClientCredentials e retornada pela propriedade ClientCredentials da classe ClientBase<TChannel>. O código a seguir define um certificado como credencial em um cliente usando o protocolo 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 @ {0}", 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()