Programmazione delle funzionalità di sicurezza di WCF

In questo argomento vengono descritte le attività di programmazione fondamentali usate per creare un'applicazione Windows Communication Foundation (WCF) sicura. Questo argomento illustra solo l'autenticazione, la riservatezza e l'integrità, note collettivamente come sicurezza del trasferimento. Questo argomento non descrive l'autorizzazione (ovvero il controllo dell'accesso a risorse o servizi). Per ottenere informazioni in merito, vedere Autorizzazione.

Nota

Per un'introduzione ai concetti di sicurezza, in particolare quelli correlati a WCF, vedere il set di modelli ed esercitazioni pratiche disponibili in MSDN nella pagina relativa a scenari, modelli e istruzioni di implementazione per Web Services Enhancements (WSE) 3.0.

La programmazione delle funzionalità di sicurezza di WCF si basa su tre passaggi: impostazione della modalità di sicurezza, impostazione di un tipo di credenziale client e impostazione dei valori di credenziale. Questi passaggi possono essere eseguiti in codice o in configurazione.

Impostazione della modalità di sicurezza

Di seguito vengono descritti i passaggi generali per programmare la modalità di sicurezza in WCF:

  1. Scegliere fra le associazioni predefinite un'associazione appropriata ai requisiti dell'applicazione. Per un elenco delle scelte di binding, vedere Binding forniti dal sistema. Per impostazione predefinita, quasi tutte le associazioni presentano un meccanismo di sicurezza abilitato. L'unica eccezione è la classe BasicHttpBinding (usando la configurazione, <basicHttpBinding>).

    L'associazione scelta determina il trasporto. Ad esempio, l'associazione WSHttpBinding utilizza il protocollo HTTP come trasporto, mentre l'associazione NetTcpBinding utilizza il trasporto TCP.

  2. Selezionare per l'associazione una delle modalità di sicurezza. Si noti che l'insieme delle modalità fra cui è possibile scegliere dipende dall'associazione scelta. Ad esempio, l'associazione WSDualHttpBinding non consente di scegliere la sicurezza a livello di trasporto. Analogamente, né l'associazione MsmqIntegrationBinding né l'associazione NetNamedPipeBinding consentono di scegliere la sicurezza a livello di messaggio.

    Sono disponibili tre opzioni:

    1. Transport

      La sicurezza a livello di trasporto dipende dal meccanismo utilizzato dall'associazione scelta. Ad esempio, se si utilizza l'associazione WSHttpBinding, il meccanismo di sicurezza è Secure Sockets Layer (SSL), che peraltro è anche il meccanismo del protocollo HTTPS. In generale, il vantaggio principale della sicurezza a livello di trasporto è che offre una buona velocità effettiva indipendentemente dal trasporto utilizzato. Tuttavia, tale tipo di sicurezza presenta due limiti. Anzitutto, il meccanismo di trasporto impone il tipo di credenziale utilizzato per autenticare un utente. Di fatto ciò rappresenta uno svantaggio solo se un servizio deve garantire l'interoperabilità con altri servizi che richiedono vari tipi di credenziali. Inoltre, poiché la sicurezza non viene applicata a livello di messaggio, la sicurezza viene implementata in modo hop-by-hop anziché end-to-end. Questo secondo limite rappresenta un problema solo se il percorso dei messaggi fra client e servizio prevede intermediari. Per altre informazioni sul trasporto da usare, vedere Scelta di un trasporto. Per altre informazioni sulla sicurezza del trasporto, vedere Panoramica della sicurezza del trasporto.

    2. Message

      Nella sicurezza a livello di messaggio, ogni messaggio contiene le intestazioni e i dati necessari a garantire la sicurezza del messaggio. Poiché la composizione delle intestazioni è variabile, è possibile includere qualsiasi numero di credenziali. Ciò risulta essere un fattore rilevante se occorre interoperare con altri servizi che richiedono un tipo di credenziale specifico che un meccanismo di trasporto non è in grado di fornire, oppure se il messaggio deve essere utilizzato in più servizi, ognuno avente un requisito specifico di tipo di credenziale.

      Per altre informazioni, vedere Sicurezza dei messaggi.

    3. TransportWithMessageCredential

      Questa scelta utilizza il livello di trasporto per proteggere il trasferimento dei messaggi, ognuno dei quali contiene le credenziali dettagliate richieste dagli altri servizi. Ciò combina il vantaggio in termini di prestazioni della sicurezza a livello di trasporto con il vantaggio delle credenziali dettagliate della sicurezza a livello di messaggio. Questa opzione è disponibile per le associazioni seguenti: BasicHttpBinding, WSFederationHttpBinding, NetPeerTcpBinding e WSHttpBinding.

  3. Se si decide di utilizzare la sicurezza a livello di trasporto per il protocollo HTTP (ovvero il protocollo HTTPS), è inoltre necessario configurare l'host con un certificato SSL e quindi abilitare il protocollo SSL su una porta. Per altre informazioni, vedere Sicurezza del trasporto HTTP.

  4. Se si utilizza l'associazione WSHttpBinding e non occorre stabilire una sessione protetta, impostare la proprietà EstablishSecurityContext su false.

    Una sessione protetta si verifica quando un client e un servizio creano un canale utilizzando una chiave simmetrica, ovvero quando sia il client sia il server utilizzano la stessa chiave per la durata della conversazione e fino alla chiusura del dialogo.

Impostazione del tipo di credenziale client

Selezionare un tipo di credenziale client in base alle proprie esigenze. Per altre informazioni, vedere Selezione di un tipo di credenziale. Sono disponibili i tipi di credenziale client seguenti:

  • Windows

  • Certificate

  • Digest

  • Basic

  • UserName

  • NTLM

  • IssuedToken

L'impostazione del tipo di credenziale dipende dalla modalità impostata. Se ad esempio è stata selezionata l'associazione wsHttpBinding e la modalità è stata impostata sulla sicurezza a livello di messaggio, l'attributo clientCredentialType dell'elemento Message può essere impostato su uno dei valori seguenti: None, Windows, UserName, Certificate e IssuedToken, come mostrato nell'esempio di configurazione seguente.

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

Oppure nel codice:

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

Impostazione dei valori di credenziale del servizio

Dopo aver selezionato un tipo di credenziale client è necessario impostare le credenziali effettivamente utilizzate dal servizio e dal client. Nel servizio, le credenziali vengono impostate utilizzando la classe ServiceCredentials e restituite tramite la proprietà Credentials della classe ServiceHostBase. L'associazione utilizzata determina il tipo di credenziale del servizio, la scelta della modalità di sicurezza e il tipo di credenziale client. Nel codice seguente un certificato viene impostato come credenziale di servizio.

// 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

Impostazione dei valori di credenziale del client

Nel client, i valori di credenziale vengono impostati utilizzando la classe ClientCredentials e restituiti tramite la proprietà ClientCredentials della classe ClientBase<TChannel>. Nel codice seguente un certificato viene impostato come credenziale in un client che utilizza il protocollo 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()

Vedi anche