Fundamentals of WCF Security
Fundamentals of WCF Security
Windows Communication Foundation (WCF) is a secure, reliable, and scalable messaging platform for the .NET Framework 3.0. With WCF, SOAP messages can be transmitted over a variety of supported protocols including IPC (named pipes), TCP, HTTP and MSMQ. Like any distributed messaging platform, you must establish security policies for protecting messages and for authenticating and authorizing calls. This article will discuss how WCF accomplishes this.
A consistent set of fundamental security concepts apply in any distributed messaging system. Consider a message from sender (the calling application) to receiver (the target service receiving the message for processing):
- Authentication. We typically think about authentication as identifying the message sender. Mutual authentication involves authenticating both the sender and the message receiver, to prevent possible man-in-the-middle attacks.
- Authorization. After authenticating the message sender, authorization determines what system features and functionality they are entitled to execute.
- Integrity. Messages should be digitally signed to ensure they have not been altered between sender and receiver.
- Confidentiality. Sensitive messages or specific message parts should be encrypted to ensure they cannot be openly viewed on the wire.
WCF provides a rich and configurable environment for creating security policies and setting runtime behaviors to control these security features. A variety of mutual authentication mechanisms are supported using token formats such as Windows tokens, username and password, certificates and issued tokens (in a federated environment). Authorization can be based on Windows roles, ASP.NET roles or you can provide custom authorization policies. Message protection (integrity and confidentiality) can be based on symmetric session keys, or asymmetric keys for single-hop protection.
| " |
“A consistent set of fundamental security concepts apply in any distributed messaging system.”
|
" |
In the following sections, I’ll show you how to configure WCF security and then take you through some common WCF deployment scenarios and their specific security configurations that employ these fundamental security concepts.
Security, WCF Style
The first step to securing a WCF service is defining the security policy. Once you have established requirements for authentication, authorization, and message protection it is a matter of service configuration to enforce it.
Your binding selection will influence the available configuration options for the service security policy. When you expose a service endpoint you select a binding that represents the appropriate communication protocol and message encoding format. For example, for intranet communications or systems behind the firewall, TCP protocol with binary message encoding is usually preferred. For Internet access, HTTP protocol is a typical choice using text or MTOM encoding (depending on the message size).
There are a standard set of bindings that can satisfy these protocol and encoding choices. NetTcpBinding is the right choice for binary TCP communications that cross machine boundaries, BasicHttpBinding is the right choice for HTTP communications that must support legacy Web service protocols, and WSHttpBinding or WSFederationHttpBinding are the right choice for Web services that can leverage a richer set of standards including those for secure communications (the latter is used for federated security scenarios).
Beyond bindings, behaviors also provide information about client and service credentials, and affect how authorization is handled.
You can configure bindings and behaviors declaratively or through the runtime object model-but in the following sections I’ll focus on how you declaratively configure core security settings.
Default Security Settings
Each binding has a default set of security settings. Consider the following service endpoint that supports NetTcpBinding.
<system.serviceModel>
<services>
<service
name="HelloIndigo.HelloIndigoService" >
<endpoint
contract="HelloIndigo.IHelloIndigoService"
binding="netTcpBinding" />
</service>
</services>
</system.serviceModel>
NetTcpBinding is secure by default. Specifically, callers must provide Windows credentials for authentication and all message packets are signed and encrypted over TCP protocol. Look at the expanded binding configuration illustrating these default settings.
<netTcpBinding>
<binding name="netTcp">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
When the security mode is set to message security, you can customize the default security settings for NetTcpBinding by configuring different values for clientCredentialType or algorithm suite. Other bindings such as WSHttpBinding also allow you to determine if a secure session will be established and control how service credentials are negotiated. Each of the standard WCF bindings supports only relevant security options for their typical usage. In the next sections, I’ll review some of the security-specific binding options available, and how you configure them.
Security Mode
Across all service bindings there are five possible security modes:
- None. Turns security off.
- Transport. Uses transport security for mutual authentication and message protection.
- Message. Uses message security for mutual authentication and message protection.
- Both. Allows you to supply settings for transport and message-level security (only MSMQ supports this).
- TransportWithMessageCredential. Credentials are passed with the message and message protection and server authentication are provided by the transport layer.
- TransportCredentialOnly. Client credentials are passed with the transport layer and no message protection is applied.
You can turn off security completely, or allocate authentication and message protection between transport and message-level security. Each transport protocol (TCP, IPC, MSMQ, or HTTP) has their own mechanism for passing credentials and handling message protection. Message security supports passing credentials as part of the SOAP message using interoperable standards, and also makes it possible to protect the message independent of transport all the way through to the ultimate message receiver. Transport message protection is only good from point to point.
| " |
“NetTcpBinding is secure by default. Specifically, callers must provide Windows credentials for authentication and all message packets are signed and encrypted over TCP protocol.”
|
" |
To control the security mode used for a particular binding, set the mode property for the binding’s <security> section. For Transport or modes that use transport security, the <transport> section should be expanded. You can see this illustrated in the <netTcpBinding> section shown earlier. For Message mode, settings are supplied in the expanded <message> section. For example, this <wsHttpBinding> snippet illustrates how to require UserName credentials be passed with the message.
<wsHttpBinding>
<binding name="wsHttp">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
NOTE: Not all bindings support all security modes.
Client Credential Type
The choice of client credential type depends on the security mode in place. For transport security you can require a Windows credential or certificate-and there are variations on how Windows credentials are passed via TCP, HTTP and MSMQ. Message security supports any of the following settings for clientCredentialType:
- None
- Windows
- UserName
- Certificate
- IssuedToken
BasicHttpBinding only supports UserName and Certificate credentials since it is intended to be interoperable with Basic Security Profile per WS-I.
This code snippet illustrates how to select a clientCredentialType for message security.
<basicHttpBinding>
<binding name="basicHttp">
<security
mode="TransportWithMessageCredential">
<message
clientCredentialType="Certificate"/>
</security>
</binding>
</basicHttpBinding>
Your choice of credential type may affect other configuration settings for the service. For example, UserName credentials require either transport message protection or a service certificate be used to protect the exchange.
Protection Level
By default, all secure WCF bindings will encrypt and sign messages. You cannot disable this for transport security, however, for message security you may wish to disable this for debugging purposes, or when an alternate method of protection is used such as IPSec.
Protection-level settings are controlled by the contract. You can specify protection level for all operations in the service contract using the ServiceContractAttribute. The following example illustrates disabling encryption.
[ServiceContract(Name="HelloIndigoContract",
Namespace=
"http://www.thatindigogirl.com/2006/06/Samples",
ProtectionLevel=ProtectionLevel.Sign)]
public interface IHelloIndigoService
{
string HelloIndigo(string inputString);
}
For more granular control, you can also indicate message protection per operation using the OperationContractAttribute.
[ServiceContract(Name="HelloIndigoContract",
Namespace=
http://www.thatindigogirl.com/2006/06/Samples]
public interface IHelloIndigoService
{
[OperationContract(ProtectionLevel=
ProtectionLevel.Sign)]
string HelloIndigo(string inputString);
}
You can also control protection level on message contracts providing granular control over specific headers or body elements.
ProtectionLevel options are: None, Sign, and EncryptAndSign. None disables message protection. EncryptAndSign provides full message protection and is the default behavior. Sign indicates the message should be signed but not encrypted.
The ProtectionLevel property serves a dual role. First, it specifies the minimum requirements for message protection. If the property is not specified, the default behavior is to encrypt and sign, but not enforce those settings on the binding. By specifying a value for the property using one of the attributes I mentioned, messages that don’t meet the requirement will be rejected for not satisfying the security policy. The second role is to control how message-level protection is applied (since it has no direct affect on transport protection).
Algorithm Suite
Choice of algorithm suite can be particularly important for interoperability. Each binding uses Basic256 as the default algorithm suite for message-level security. This suite defines the algorithms and key lengths for cryptographic operations like key signatures, encryption, and key wrap. Algorithm suites are described in the WS-SecurityPolicy specification, and can be applied by setting the algorithm attribute of the <message> section.
<wsHttpBinding>
<binding name="wsHttp">
<security mode="Message">
<message clientCredentialType="UserName"
algorithmSuite="TripleDes" />
</security>
</binding>
</wsHttpBinding>
Service Credentials and Negotiation
To support mutual authentication and message protection, services must provide credentials to the caller. When transport security is used, service credentials are negotiated through the transport protocol. Service credentials for message security can also be negotiated when Windows credentials are used, otherwise a service certificate must be specified in the <behaviors> section under <serviceCredentials>.
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior" >
<serviceCredentials>
<serviceCertificate findValue="RPKey"
storeLocation="LocalMachine" storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
In this case, the caller must have access to the public key portion of the service certificate to encrypt messages sent to the service. This can be specified out of band, or negotiated with an initial handshake.
The default behavior for message security supports negotiation. That means that the service is dynamically asked for the correct token before any messages are exchanged. For Windows client credentials SPNego protocol is used, and for UserName, Certificate or Anonymous credentials, TLSNego protocol is used. Today these are not interoperable protocols, so it may be desirable to disable this negotiation.
You can set negotiateServiceCredential to false in the <message> section to accomplish this.
<wsHttpBinding>
<binding name="wsHttp">
<security mode="Message">
<message clientCredentialType="UserName"
negotiateServiceCredential="false" />
</security>
</binding>
</wsHttpBinding>
When negotiation is disabled for Windows client credentials, a Kerberos domain must exist. For other credential types the client must have access to the service public key to encrypt messages.
When you generate a service proxy with configuration settings for the client (using svcutil.exe) an encoded version of the public certificate is supplied in the <identity> section to handle this case.
<client>
<endpoint
address="http://localhost:8000/HelloIndigo"
binding="wsHttpBinding"
bindingConfiguration="wsHttp"
contract="Client.localhost.HelloIndigoContract"
name="WSHttpBinding_HelloIndigoContract">
<identity>
<certificate encodedValue="
AwAAAAEAAAAUAAAAreiGqilku9hngWEQL1g+
…
oBd0vDwZaqjy47g0jFV9pF0VHhoVbTtOA=="/>
</identity>
</endpoint>
</client>
It is also possible to install the public key of the service in the client certificate store and retrieve it from there at run time.
Secure Session
Another feature of message security is the ability to establish a secure session to reduce the overhead of one-off key exchange and validation. By default, secure sessions are enabled for message security. A security context token (SCT) is generated through an initial exchange between caller and service. This token is used to authorize and secure subsequent message exchanges.
If the caller plans to make several calls to a service, secure sessions are more efficient. For a single call, however, you can disable this feature by setting establishSecurityContext to false.
<wsHttpBinding>
<binding name="wsHttp">
<security mode="Message">
<message clientCredentialType="UserName"
establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
|
Authentication, Authorization, and Identities
From the discussion so far, you should gather that messages are secured according to service security policy. Mutual authentication is performed based on the supplied client and service credentials. Message protection is applied according to transport or message security configuration-which normally means that messages are both signed and encrypted. Token authentication, runtime identities, security principals, and authorization policies also play an important role in the WCF security story.
| " |
“Token authentication, runtime identities, security principals and authorization policies also play an important role in the WCF security story.”
|
" |
Access to resources during a service operation is influenced by three key elements:
- Process Identity. Service operations are executed under the process identity of the service host. For ASP.NET hosts this is usually the ASP.NET account, and for self-hosting it may be a different service account. This process identity is the Windows account that governs what the service code can do at run time when attempting to access protected resources such as the database, registry or file system.
- Security Principal. If you are familiar with traditional .NET role-based security, you know that there is a security principal attached to each executing thread. That security principal holds the caller’s identity, which may or may not be tied to a Windows account and its roles. Roles govern which operations can be executed by the authenticated user when traditional .NET role-based security is applied.
- ServiceSecurityContext. Provides run time access to other relevant information about the security context for a service operation. The ServiceSecurityContext is a run time type that includes identities, a set of claims, and authorization policies. This information can be used to apply a more fine-grained security strategy specifically for services.
Figure 1 illustrates the relationship of these security elements. While the process identity is constant, each operation is executed on a request thread that contains a unique security principal and security context. In the following sections I’ll elaborate on the authorization process and the role of these and other security elements.
IMG1
Figure 1: Operations are executed within a security context that includes a set of claims and an identity. The identity of the security principal attached to the executing thread is equivalent to the primary identity of the security context.
Claims-Based Identity Model
The identity model in WCF supports a rich, claims-based approach to authorization. A claim describes an individual right or action applicable to a particular resource. For example, an identity claim could represent a Windows token, a non-Windows user account, an X509 certificate or some other identity type. Claims can also be proof of possession of other information such as an e-mail address, birth date, or first and last name. Custom claims can be created to indicate the ability to access specific business entities or their storage location.
Claims are generated from security tokens. Security tokens are abstractions of credentials that are passed in the security headers of a message and validated against the security policy. When security tokens are validated and processed at the service, claims are extracted and placed into the security context for the operation being executed. Each credential type will result in a different set of claims (called a claimset) to evaluate-but you could unify the claimset by mapping the default claims to custom claims that will be evaluated by your code at run time.
Ultimately, a claimset is attached to the ServiceSecurityContext and available for any custom authorization code involved in the execution of the operation. Although traditional role-based security is still supported, a customized claims-based authorization model can add a welcome layer of granularity.
ServiceSecurityContext
ServiceSecurityContext provides access to claims at run time. Some key properties of the ServiceSecurityContext type are:
AuthorizationContext. Contains one or more claimsets for authorization. Use this information to perform custom authorization.
AuthorizationPolicies. Contains the policies used to grant claims.
PrimaryIdentity. Contains the identity claim from the claim set, as a traditional IIdentity reference.
WindowsIdentity. Contains the identity claim from the claim set if it is a WindowsIdentity.
You can access the ServiceSecurityContext through the OperationContext.
ServiceSecurityContext security =
OperationContext.Current.ServiceSecurityContext;
With that reference, you could implement a custom authorization check that is based on claims. For example, you could check to see that the user was authenticated and that an e-mail claim was also provided.
string user = security.PrimaryIdentity.Name;
string email = null;
IEnumerable<Claim> claims = security.
AuthorizationContext.ClaimSets[0].FindClaims
(ClaimTypes.Email,Rights.PossessProperty);
foreach (Claim c in claims)
{
email = c.Resource as string;
}
if (string.IsNullOrEmpty(user) || email == null)
throw new SecurityException
("Unauthorized access. Email claim not found.");
This code illustrates using the ServiceSecurityContext for custom authorization inside an operation-but it can also be wrapped into a custom authorization policy to decouple from operation code and provide a unit of reuse.
The ServiceSecurityContext is also used during role-based authorization, to be discussed shortly.
Security Token Authentication
Claims are added to the security context while tokens are authenticated. One or more security tokens can be present in a message. Each token is authenticated by its own SecurityTokenAuthenticator type. Table 1 provides a list of commonly used token authenticators with a short description.
Windows tokens are authenticated against the Windows domain. Certificate credentials are authenticated against the certificate store based on authentication rules specified in the <clientCertificate> section of the service behavior. For example, the following configuration does not map certificates to Windows accounts, and will trust certificates placed in the TrustedPeople store-otherwise it will try to validate the chain of trust online.
<behavior name="serviceBehavior">
<serviceCredentials>
<clientCertificate>
<authentication
certificateValidationMode="PeerOrChainTrust"
trustedStoreLocation="LocalMachine"
revocationMode="Online"
mapClientCertificateToWindowsAccount="false" />
</clientCertificate>
Wednesday, August 13, 2008 2:24:44 PM
|
|
|