Chapter 15: Internet - Windows Forms Client to Remote WCF Using Message Security (Original Caller, HTTP)
Applies To
- Microsoft® Windows Communication Foundation (WCF) 3.5
Scenario
In this scenario, your users do not have Microsoft Windows® accounts and use a Windows Forms client to connect over the Internet to your WCF service. The business logic called by the WCF service is backed by a Microsoft SQL Server® data store. The basic model for this application scenario is shown in the following figure.
Figure 1
Windows Forms Client to Remote WCF Using Message Security (Original Caller, HTTP) – Model
Key Characteristics
This scenario applies to you if:
- Your users have Windows Forms clients.
- Your user accounts are stored in SQL Server.
- Your user roles are stored in SQL Server.
- Your application transmits sensitive data over the network that needs to be protected.
- The ability to host the WCF service in internet Information Services (IIS) is more important than a high-performance connection between the ASP.NET application and the WCF service.
Solution
Figure 2
Windows Forms Client to Remote WCF Using Message Security (Original Caller, HTTP) – Solution
Solution Summary Table
In this solution, you will:
- Use a username and password to authenticate users against the SQL Server membership provider.
- Use a service account to call the SQL Server from WCF.
- Use message security to protect sensitive data between the ASP.NET application and the WCF service.
- Use wsHttpBinding to allow IIS to host the service.
- Host WCF in IIS.
Thick Client
Checks / more information |
Example |
---|---|
WCF proxy |
|
The thick client has a proxy reference to the WCF service. The application has access to the WCF metadata to create a service reference. |
|
A root certificate authority (CA) certificate for the service is installed in Trusted Root Certification Authorities. All certificates that are signed with this certificate will be trusted by the client machine. |
|
Pass user credentials to the WCF service when calling service operations. A proxy will invoke a WCF method within the service contained on the application server by using the Service Accounts security context. |
|
Application Server
Checks / more information |
---|
A dedicated application pool is created and configured to run under a custom service account. Use a domain account if possible. |
The WCF service is configured to run under the service account. Assign the WCF service to the custom application pool. |
The IIS virtual directory is configured to use Anonymous access. |
Checks / more information |
Example |
---|---|
WCF service—configuration |
|
An ASP.NET database is created for use with the SQL Server membership provider and SQL Server role provider. Aspnet_regsql.exe creates the SQL Server database to store the user and role information. |
|
A connection string is configured to point to the user and role stored in SQL Server. The database connection string includes Integrated Security=SSPI or Trusted Connection=Yes for Windows Authentication. |
|
SqlMembershipProvider is configured to be used with the membership feature. The membership feature helps protect credentials, can enforce strong passwords, and provides consistent APIs for user validation and secure user management. |
|
The Role Manager feature is enabled and SqlRoleProvider is configured for roles authorization. Role Manager allows you to look up users’ roles without writing and maintaining custom code. |
|
The WCF service process identity is given access permissions to the ASP.NET database. Your WCF service process identity requires access to the aspnetdb database. |
|
The WCF service is configured to use wsHttpBinding. wsHttpBinding uses the HyperText Transfer (HTTP) protocol and provides full support for Simple Object Access Protocol (SOAP) security, transactions, and reliability. Because clients are on the Internet, this is the only choice. |
|
WCF service—authentication |
|
wsHttpBinding is configured to use username authentication and message security. |
|
SqlMembershipProvider is configured to provide user authentication. The membership feature automatically authenticates and creates the authentication ticket for you. |
|
Service behavior is configured to use MembershipProvider for use with username authentication. |
|
The service behavior is configured to publish metadata. |
|
A service certificate is installed on the WCF service machine. The service behavior is configured to use the service certificate. This is required for protecting the user credentials in the message. |
|
WCF service—authorization |
|
The service behavior is configured to use AspNetRoles with SqlRoleProvider. |
|
WCF operations are configured to declaratively perform role checks at the operation level. Declarative role checks on operations is the preferred mechanism. |
|
Roles APIs are used to perform programmatic role checks, for fine-grained access control. If you need finer-grained authorization control, you can use imperative role checks in the code itself. Use a call to Roles.IsUserInRole to perform the check. |
|
WCF service—SQL |
|
The connection string for database is configured to use Windows authentication. The database connection string includes Integrated Security=SSPI or Trusted Connection=Yes. |
|
Open the database connection by using the WCF process identity’s security context. The service does not impersonate the original caller to benefit from connection pooling. |
Database Server
Checks / more information |
Example |
---|---|
Configuration |
|
A SQL Server login is created for the WCF’s service account (process identity). This grants access to the SQL Server. |
|
The login is mapped to a database user for the Web application. This grants access to the specified database. |
|
A database role is created in the target database. This allows access control and authorization to the database. |
|
The login is added to the database role. Grant minimum permissions. For example, grant execute permissions to selected stored procedures, and provide no direct table access. |
|
Authentication |
|
SQL Server is configured to use Windows authentication. |
Communication Security
What |
Checks |
---|---|
Application server to database server |
You can use Internet Protocol security (IPSec) or secure Sockets layer (SSL) encryption between the application server and the database server in order to protect sensitive data on the wire. |
Analysis
Thick Client
WCF Proxy
- Because the original user’s credentials are required in WCF for authentication and authorization, username credentials are set on the WCF proxy and all calls to the WCF service are made through that proxy instance.
- For validating the service certificate, the root CA certificate is installed on the client machine in the Trusted Root Certification Authorities location.
Application Server
Authentication
- Because the users are coming from the Internet and you cannot assume that they have a Windows account, the user information is stored in SQL Server. For this reason, WCF is configured to use username authentication to authenticate its callers.
- The membership feature is a good choice as it allows you to enable username authentication without writing and maintaining custom code.
- To protect the user credentials over the wire, a service certificate is installed and configured to be used as service credentials in WCF.
Authorization
- For coarse grained access control, authorization checks are performed declaratively in the WCF service at the operation level.
- For fine-grained access control or for implementing business logic, authorization checks are made within the operations programmatically.
- The Role Manager is a good choice for this scenario because it allows you to look up users’ roles without writing and maintaining custom code.
Data Access
- To reduce the chances of database credentials being stolen, the database connection string is configured to use Windows authentication. This eliminates the need to store credentials in files and pass credentials over the network to the database server.
- The WCF service accesses the database using the WCF process identity. As a result, all calls are made using the single process account and database connection pooling.
Configuration
- Since all of the clients are coming from the Internet, the best transport protocol for this scenario is HTTP. For this reason, wsHttpBinding is an ideal choice.
- Because wsHttpBinding is supported by IIS 6.0, Microsoft hosted the WCF service in IIS.
- In order to reduce the attack surface and minimize the impact of a compromise, the WCF service is running under the security context of the service account, using a least-privileged account.
Database Server
- SQL Server database user roles are preferred over SQL Server application roles to avoid the password management and connection pooling issues associated with the use of SQL Server application roles. Applications activate SQL Server application roles by calling a built-in stored procedure with a role name and a password. Therefore, the password must be stored securely. Database connection pooling must also be disabled when you use SQL Server application roles, which severely impacts application scalability.
- Creating a new user-defined database role and adding the database user to the role lets you give specific minimum permissions to the role. In this way, if the database account changes, you do not have to change the permissions on all database objects.
Communication Security
- Message security protects sensitive data between the thick client and the WCF service.
- You can use IPSec or SSL between the WCF service and the database server to protect sensitive data on the wire.
Example
Application Server
Code
- The service performs imperative authorization checks, calling Roles.IsUserInRole.
- If auditing is required, the service retrieves the identity of the caller.
- The service calls SQL Server using Windows authentication.
using System.Data.SqlClient;
using System.Web.Security;
public string GetData(int value)
{
if (Roles.IsUserInRole(@"accounting"))
{
SqlConnection sqlcon = new SqlConnection("Server=10.3.19.60;Database=testdb;Integrated Security=SSPI");
sqlcon.Open();
string identity = ServiceSecurityContext.Current.PrimaryIdentity.Name;
return “data”
}
else return "not authorized";
}
}
Configuration
- The service has a binding endpoint that uses wsHttpBinding with binding configuration that enables message security and username authentication.
- The service configuration file has an entry with a connection string pointing to the SQL store for authentication and authorization.
- The service configuration file has an entry for the SqlRoleProvider under system.web to define which role provider is being used.
- The service configuration file has an entry for the SqlMemberShipProvider under system.web to define the SQL Server provider for authentication.
- The service has a service behavior to use the SqlMemberShipProvider.
- The service behavior is configured with the serviceAuthorization element to allow UseAspNetRoles as the authorization provider.
- The service behavior is configured with the serviceMetadata element to allow publishing of metadata.
- The service behavior is configured to use a certificate to encrypt the messages.
<configuration>
…
<connectionStrings>
<add name="MyLocalSQLServer"
connectionString="Initial Catalog=aspnetdb;data source=10.3.19.60;Integrated Security=SSPI;"/>
</connectionStrings>
<system.web>
<membership defaultProvider="MySqlMembershipProvider" >
<providers>
<clear/>
<add name="MySqlMembershipProvider"
connectionStringName="MyLocalSQLServer"
applicationName="MyAppName"
type="System.Web.Security.SqlMembershipProvider" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="MySqlRoleProvider" >
<providers>
<clear/>
<add name="MySqlRoleProvider"
connectionStringName="MyLocalSQLServer"
applicationName="MyAppName"
type="System.Web.Security.SqlRoleProvider" />
</providers>
</roleManager>
<assemblies>
<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral,PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</controls>
</pages>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="BindingConfiguration">
<security>
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="BehaviorConfiguration" name="Service">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="BindingConfiguration"
name="WsBinding" contract="IService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BehaviorConfiguration">
<serviceAuthorization principalPermissionMode="UseAspNetRoles"
roleProviderName="MySqlRoleProvider" />
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="CN=perfpres02.npscode.com" />
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="MySqlMembershipProvider" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Client
Code
- The client passes user credentials explicitly when making calls to the service.
WCFTestService.ServiceClient myService = new WCFTestService.ServiceClient();
myService.ClientCredentials.UserName.UserName = "username";
myService.ClientCredentials.UserName.Password = "p@ssw0rd";
myService.GetData(123);
myService.Close();
Database Server
Configuration
- A SQL Server login is created for the WCF service account.
- The WCF login name is given access to the application database.
- The role is created in the application database.
- The WCF login name is added to the role.
-- Create a SQL Server login that matches the WCF machine name
EXEC SP_GRANTLOGIN 'npscode\perfpres02$'
-- Grant the login access to the application database
use testdb
go
exec sp_grantdbaccess 'npscode\perfpres02$'
-- Create the new database role
use testdb
go
exec sp_addrole 'myrole2','db_owner'
-- Add the new login to the role
use testdb
go
exec sp_addrolemember 'myrole2','npscode\perfpres02$'
Additional Resources
- For more information on the WCF role service, see Windows Communication Foundation Role Service Overview.
- For more information on ASP.NET role management, see ASP.NET: Understanding Role Management.
- For more information on Windows authentication, see Explained: Windows Authentication in ASP.NET 2.0.
- For more information on debugging authentication errors, see Debugging Windows Authentication Errors.
- For more information on security authentication best practices, see Best Practices for Security in WCF.
- For additional information on message security, see Message Security in WCF.