Programmation de la sécurité dans WCF

Cette rubrique répertorie les principales tâches de programmation à effectuer pour créer une application Windows Communication Foundation (WCF) sécurisée. Cette rubrique traite uniquement de l’authentification, de la confidentialité et de l’intégrité, désignées collectivement par le terme sécurité de transfert. Cette rubrique n’aborde pas l’autorisation (c’est-à-dire le contrôle d’accès aux ressources ou aux services). Pour plus d’informations sur l’autorisation, consultez Autorisation.

Notes

Pour obtenir une présentation correcte des concepts de sécurité, notamment concernant WCF, consultez l’ensemble des didacticiels portant sur les modèles et pratiques sur MSDN dans la rubrique Scénarios, modèles et guide d’implémentation de Web Services Enhancements (WSE) 3.0.

La programmation de la sécurité WCF s’effectue en trois étapes : mode de sécurité, type d’informations d’identification client et valeurs des informations d’identification. Ces étapes peuvent être effectuées au choix dans le code ou la configuration.

Définition du mode de sécurité

Les explications ci-dessous portent sur les étapes générales de la programmation du mode de sécurité dans WCF :

  1. Sélectionnez une liaison prédéfinie adaptée aux exigences de votre application. Pour obtenir la liste des choix de liaisons, consultez Liaisons fournies par le système. Par défaut, la sécurité de pratiquement toutes les liaisons est activée. La classe BasicHttpBinding fait exception à cette règle (dans la configuration, l’élément <basicHttpBinding>).

    Le transport dépend de la liaison sélectionnée. Par exemple, WSHttpBinding utilise le transport HTTP tandis que NetTcpBinding utilise le transport TCP.

  2. Sélectionnez l'un des modes de sécurité disponibles pour la liaison. Remarque : les modes de sécurité disponibles dépendent de la liaison sélectionnée. Par exemple, la liaison WSDualHttpBinding ne permet pas de définir le niveau transport comme mode de sécurité (ce mode n'est pas disponible pour cette liaison). De la même façon, les liaisons MsmqIntegrationBinding et NetNamedPipeBinding ne permettent pas de définir le niveau message comme mode de sécurité.

    Trois choix sont possibles :

    1. Transport

      La sécurité de transport dépend du mécanisme utilisé par la liaison sélectionnée. Par exemple, si vous utilisez la liaison WSHttpBinding, la sécurité utilisée correspondra à Secure Sockets Layer (SSL) (il s'agit également de la sécurité utilisée pour le protocole HTTPS). De manière générale, le principal avantage de la sécurité de niveau transport réside dans le fait qu'elle offre un débit relativement élevé quel que soit le transport utilisé. Elle présente cependant deux inconvénients. Le premier réside dans le fait que le mécanisme de transport impose quel type d'informations d'identification doit être utilisé pour authentifier un utilisateur. Cela pose problème uniquement lorsque le service concerné doit interagir avec d'autres services exigeant un autre type d'informations d'identification. Le second réside dans le fait que la sécurité est implémentée saut par saut plutôt que de bout en bout. Cela peut poser un problème lorsque les messages circulant entre le client et le service concernés rencontrent des intermédiaires. Pour plus d’informations sur le transport à utiliser, consultez Choix d’un transport. Pour plus d’informations sur l’utilisation de la sécurité du transport, consultez Vue d’ensemble de la sécurité du transport.

    2. Message

      La sécurité de niveau message signifie que chaque message comporte les en-têtes et les données nécessaires à sa sécurisation. La composition des en-têtes variant, vous pouvez ajouter aux messages un nombre illimité d'informations d'identification. Cette possibilité est essentielle lorsque votre service doit interagir avec des services exigeant un type d'informations d'identification spécifique qu'un mécanisme de transport ne peut fournir ou lorsqu'un message doit être utilisé par plusieurs services, chacun d'eux exigeant un type d'informations d'identification différent.

      Pour plus d’informations, consultez l’article Sécurité des messages.

    3. TransportWithMessageCredential

      Ce mode utilise la couche de transport pour sécuriser le transfert des messages, tandis que chaque message inclut l'ensemble des informations d'identification requises par les autres services. Ce mode allie les avantages de la sécurité de niveau transport aux avantages présentés par la sécurité de niveau message, notamment celui d'utiliser un nombre illimité d'informations d'identification. Ce mode est disponible avec les liaisons suivantes : BasicHttpBinding, WSFederationHttpBinding, NetPeerTcpBinding et WSHttpBinding.

  3. Si vous choisissez d’utiliser la sécurité de transport pour HTTP, c’est-à-dire HTTPS, vous devez également configurer l’hôte avec un certificat SSL et activer ce certificat sur l’un des ports de votre ordinateur. Pour plus d’informations, consultez Sécurité du transport HTTP.

  4. Si vous utilisez la liaison WSHttpBinding et que vous n'avez pas besoin d'établir une session sécurisée, affectez à la propriété EstablishSecurityContext la valeur false.

    Une session sécurisée intervient lorsqu'un client et un service créent un canal à l'aide d'une clé symétrique (le client et le serveur utilisent la même clé pendant toute la durée de la conversation, jusqu'au terme de celle-ci).

Définition du type d'informations d'identification client

Sélectionnez un type d'informations d'identification client comme requis. Pour plus d’informations, consultez Sélection d’un type d’informations d’identification. Les types suivants d'informations d'identification client sont disponibles :

  • Windows

  • Certificate

  • Digest

  • Basic

  • UserName

  • NTLM

  • IssuedToken

Vous devez définir le type d'informations d'identification en fonction du mode de sécurité que vous avez sélectionné. Par exemple, si vous avez sélectionné la liaison wsHttpBinding et que vous avez affecté au mode de sécurité la valeur Message, vous pouvez alors affecter à l'attribut clientCredentialType de l'élément de message l'une des valeurs suivantes : None, Windows, UserName, Certificate et IssuedToken, comme illustré dans l'exemple de configuration suivant.

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

Ou dans le code :

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

Définition des valeurs d'informations d'identification service

Après avoir sélectionné un type d'informations d'identification client, vous devez définir les informations d'identification que le client et le service devront effectivement utiliser. Du côté service, les informations d'identification sont définies à l'aide de la classe ServiceCredentials et sont retournées par la propriété Credentials de la classe ServiceHostBase. Les types d'informations d'identification service et client ainsi que le mode de sécurité dépendent de la liaison utilisée. L'exemple de code suivant définit un certificat pour les informations d'identification service.

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

Définition des valeurs d'informations d'identification client

Du côté client, les informations d'identification sont définies à l'aide de la classe ClientCredentials et sont retournées par la propriété ClientCredentials de la classe ClientBase<TChannel>. L'exemple de code suivant définit un certificat pour les informations d'identification client, le protocole TCP étant utilisé.

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

Voir aussi