http://msdn.microsoft.com/library/ff405740.aspx
Here is 3 questions you may ask about the Security:
- Can any client call the service or do you want to control who can call the service?
- Can any client call any method of the service or do you want to control what clients can call what methods?
- Can any client execute all of the code in a method or do you want to control what clients can execute what code?
- Internal Self-Hosted Services. You are self-hosting a WCF service on the corporate network. You want anyone who can log onto the network to be able to access the service. You want only certain users to be able to call particular methods.
- Internal Web-Hosted Services. You are hosting a WCF service using Internet Information Services on the corporate intranet. Both employees and guests have access to the wireless network. You want only employees to be able to call the service. You want only certain users to be able to call particular methods.
- Public Web-Hosted Services. You are hosting a WCF service publicly on the Internet. You want to limit access to the service to users with a valid user name and password. You want only certain users to be able to call particular methods.
Authentication enables you to identify clients that call the service. Clients can identify themselves by providing evidence such as a Windows account, a user name / password or a certificate.
Authorization enables you to determine what operations authenticated clients can access. You will typically base authorization on roles.
Transport Security and Message Security
If you use transport security, security occurs at the transport level. The packets sent “on the wire” include the caller’s credentials and the message. Both of which are encrypted using whatever mechanism the transport protocol uses. For example, if you use TCP, you will likely use Transport Layer Security (TLS) and if you use HTTPS, you will likely use Secure Sockets Layer (SSL).
If you use message security, the caller’s credentials are included in the message and the message is encrypted using the WS-Security specification.
It is generally faster to encrypt and decrypt messages that use transport security and you can benefit from hardware acceleration to improve performance.
A downside to transport security is that messages are encrypted only from point to point. Suppose a client sends a message to a service. The client encrypts the message and the service decrypts it. If the service then forwards the message to another service, the service forwarding the message will not automatically encrypt it. This is not an issue with message security because the service will encrypt the message before passing it on to another service.
A downside to message security is that it requires both clients and services to support the WS-Security specification. Transport security does not have this requirement and is therefore more interoperable.
Configure Security
WCF supports the following six security modes:
- None. Messages are not secured.
- Transport. Messages are secured using transport security.
- Message. Messages are secured using message security.
- TransportWithMessageCredential. Message protection and authorization occur at the transport level and credentials are passed with the message.
- TransportCredentialOnly. Credentials are passed at the transport level but the message is not encrypted. This option is available only if you are using the BasicHttpBinding binding.
- Both. Messages are secured using both transport level and message level security. This is supported only if you are using Microsoft Message Queue Server.
Each binding has a default set of security settings. By default, the following bindings use message security: WSHttpBinding, WS2007HttpBinding, WSDualHttpBinding, WSFederationBinding and WS2007FederationBinding.
By default, the following bindings use transport security: NetTcpBinding, NetNamedPipesBinding, NetMsmqBinding, NePeerBinding and MsmqIntegrationBinding.
By default, the BasicHttpBinding binding uses None as its security mode. In other words, message sent using that binding are not secure. This enables interoperability with ASMX Web services.
Authenticate Clients and Services
Authentication enables you to identify clients and services. They identify themselves by passing credentials. WCF supports the following credential types when you are using transport level security:
- Windows. The client uses a Windows token representing the logged in user’s Windows identity. The service uses the credentials of the process identity or an SSL certificate.
- Basic. The client passes a user name and password to the service. Typically, the user will enter the user name and password in a login dialog box. The service uses a SSL certificate. This option is available only with HTTP protocols.
- Certificate. The client uses an X.509 certificate and the service uses either that certificate or an SSL certificate.
- NTLM. The service validates the client using a challenge/response scheme against Windows accounts. The service uses a SSL certificate. This option is available only with HTTP protocols.
- None. The service does not validate the client.
WCF supports the following credential types when you are using message level security:
- Windows. The client uses a Windows token representing the logged in user’s Windows identity. The service uses the credentials of the process identity or an SSL certificate.
- UserName. The client passes a user name and password to the service. Typically, the user will enter the user name and password in a login dialog box. The service can validate the user name and password using a Windows account or the ASP.NET membership provider.
- Certificate. The client uses an X.509 certificate and the service uses either that certificate or an SSL certificate.
- IssueToken. The client and service use the Secure Token Service, which issues tokens the client and service trust. Windows CardSpace uses the Secure Token Service.
- None. The service does not validate the client.
Authorize Clients
Authorization enables you to determine what operations authenticated clients can access. WCF supports three basic approaches to authorization:
- Role-based. Access to a service and to operations of the service is based on the user’s role.
- Identity based. Access is based on claims made within the user’s credentials. This is an extension to role-based authorization and provides a more fine grained approach. This approach will typically be used with issue token authentication.
- Resource based. Resources, such as WCF services, are secured using Windows Access Control Lists (ACLs).
You have three options when deciding how to determine a user’s role:
- Windows groups. You can use the built-in Windows groups such as Administrators or Power Users or create your own Windows groups.
- Custom roles. You can create roles that are specific to your application, such as Manager, Employee, Administrator, etc.
- ASP.NET role management. You can use the ASP.NET role provider and use roles you have defined for a Web site.
Client Config
<bindings>
<netTcpBinding>
...
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
<wsHttpBinding>
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
public string SayHello()
{
return string.Format("Hello {0}",
System.Threading.Thread.CurrentPrincipal.Identity.Name);
}
Server: Authorization
[PrincipalPermission(SecurityAction.Demand, Role="BUILTIN\\BackupOperators")]
public decimal ReportSales()
OR
public decimal ReportSales()
{
var currentUser = new WindowsPrincipal((WindowsIdentity)
System.Threading.Thread.CurrentPrincipal.Identity);
if (currentUser.IsInRole(WindowsBuiltInRole.BackupOperator))
{
return 10000M;
}
else
{
return -1M;
}
}
Sample 2 Private Web-Hosted Services (basic authentication)
(WCF Service Host at IIS)
2.1 basic authentication
**When you create the Web site to host the WCF service, you will use HTTPS and SSL to encrypt communications. SSL requires a digital certificate, so you must next create and specify a certificate to use.
How to turn on basic authentication at IIS: http://msdn.microsoft.com/library/ff406125.aspx
How to add a certificate: http://msdn.microsoft.com/library/ff406125.aspx
Client Code
Double-click the Log in button and add the following code to the logInButton_Click method:
// C#
proxy = new SecureServiceClient("WSHttpBinding_ISecureService");
proxy.ClientCredentials.UserName.UserName = nameTextBox.Text;
proxy.ClientCredentials.UserName.Password = passwordTextBox.Text;
Client Configuration <bindings>
<wsHttpBinding>
...
<security mode="Transport">
<transport clientCredentialType="Basic" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
Sample 3 Public Web-Hosted Services
Server Side Configuration
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;;User ID=sa;Password=password;;initial catalog=aspnetdb;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
<!--<authorization>
<deny users="?" />
</authorization>-->
<roleManager enabled="true">
<providers>
<clear/>
<add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/"/>
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/"/>
</providers>
</roleManager>
<authentication mode="Forms"/>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/"/>
</providers>
</membership>
<compilation debug="true" strict="false" explicit="true">
<assemblies>
<add assembly="System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/></assemblies></compilation>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WsHttpBindingConfig">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None"/>
<message clientCredentialType="UserName" negotiateServiceCredential="false"
establishSecurityContext="false"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="SecureServiceLibrary.SecureService" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration="WsHttpBindingConfig"
contract="SecureServiceLibrary.ISecureService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" name="MexHttpsBindingEndpoint"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="AspNetSqlMembershipProvider"/>
</serviceCredentials>
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="AspNetSqlRoleProvider"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
<listeners>
<add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\log\Traces.svclog"/>
</listeners>
</source>
</sources>
<trace autoflush="true"/>
</system.diagnostics>
</configuration>
Client Side Configuration
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ISecureService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None"/>
<message clientCredentialType="UserName" negotiateServiceCredential="false"
establishSecurityContext="false"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://[yourservername]/PublicWebHostedServiceDemo2/SecureService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ISecureService"
contract="SecureService.ISecureService" name="WSHttpBinding_ISecureService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;;User ID=sa;Password=password;;initial catalog=aspnetdb;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
<authorization>
<deny users="?" />
</authorization>
<roleManager enabled="true">
<providers>
<clear/>
<add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/"/>
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/"/>
</providers>
</roleManager>
<authentication mode="Forms"/>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/"/>
</providers>
</membership>
<compilation debug="true" strict="false" explicit="true">
<assemblies>
<add assembly="System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/></assemblies></compilation>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WsHttpBindingConfig">
<!--<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None"/>
<message clientCredentialType="UserName" negotiateServiceCredential="false"
establishSecurityContext="false"/>
</security>-->
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="true"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="SecureServiceLibrary.SecureService" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration="WsHttpBindingConfig"
contract="SecureServiceLibrary.ISecureService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" name="MexHttpsBindingEndpoint"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<serviceCertificate findValue="localhost" x509FindType="FindBySubjectName"
storeLocation="LocalMachine" storeName="My" />
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="AspNetSqlMembershipProvider"/>
</serviceCredentials>
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="AspNetSqlRoleProvider"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
<listeners>
<add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\log\Traces.svclog"/>
</listeners>
</source>
</sources>
<trace autoflush="true"/>
</system.diagnostics>
</configuration>
Client Side Configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ISecureService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport realm="" />
<message clientCredentialType="UserName" negotiateServiceCredential="true"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://[YourServerName]/PublicWebHostedServiceDemo2/SecureService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ISecureService"
contract="SecureService.ISecureService" name="WSHttpBinding_ISecureService"
behaviorConfiguration="certForClient">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="certForClient">
<clientCredentials>
<serviceCertificate >
<authentication certificateValidationMode="PeerOrChainTrust" revocationMode="NoCheck"/>
</serviceCertificate>
<clientCertificate findValue="localhost" x509FindType="FindBySubjectName"
storeLocation="CurrentUser" storeName="My"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>