Chapter 15: Internet - Windows Forms Client to Remote WCF Using Message Security (Original Caller, HTTP)

patterns & practices Developer Center

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.

Ff648163.CH15-Fig1(en-us,PandP.10).png

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

Ff648163.CH15-Fig2(en-us,PandP.10).png

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.

        
WCFTestService.ServiceClient myService = new WCFTestService.ServiceClient();

      

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.

        
myService.ClientCredentials.UserName.UserName = "username";
myService.ClientCredentials.UserName.Password = "p@ssw0rd";
myService.GetData(123);

      

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.

        
aspnet_regsql -S .\SQLExpress -E -A r m

      

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.

        
<add name="MyLocalSQLServer" connectionString="Initial Catalog=aspnetdb;data source=localhost;Integrated Security=SSPI;" />

      

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.

        
<membership defaultProvider="MySqlMembershipProvider">
    <providers>
       <clear/>
       <add name="MySqlMembershipProvider"   
       connectionStringName="MyLocalSQLServer" 
       applicationName="MyAppName"
 type="System.Web.Security.SqlMembershipProvider"/>
   </providers>
</membership>

      

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.

        
<roleManager enabled="true" defaultProvider="MySqlRoleProvider" >
    <providers>
      <clear/>
      <add name="MySqlRoleProvider"
           connectionStringName="MyLocalSQLServer"
           applicationName="MyAppName"
           type="System.Web.Security.SqlRoleProvider" />
    </providers>
</roleManager>

      

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.

        
-- Create a SQL Server login for the Network Service account
sp_grantlogin '<<Custom Service Account>>'

-- Grant the login access to the membership database USE aspnetdb GO sp_grantdbaccess '<<Custom Service Account>>', '<<Custom Service Account>>'

-- Add user to database role USE aspnetdb GO sp_addrolemember 'aspnet_Membership_FullAccess', '<<Custom Service Account>>'

sp_addrolemember 'aspnet_Roles_FullAccess', '<<Custom Service Account >>’

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.

        
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="BindingConfiguration"
          name="WsBinding" contract="IService"/>

      

WCF service—authentication

wsHttpBinding is configured to use username authentication and message security.

        
<wsHttpBinding>
 <binding name="BindingConfiguration">
  <security>
  <message clientCredentialType="UserName" />
   </security>
 </binding>
</wsHttpBinding>

      

SqlMembershipProvider is configured to provide user authentication.

The membership feature automatically authenticates and creates the authentication ticket for you.

        
<membership defaultProvider="MySqlMembershipProvider">
<providers>
<clear/>
<add name="MySqlMembershipProvider" connectionStringName="MyLocalSQLServer" applicationName="MyAppName" type="System.Web.Security.SqlMembershipProvider"/>
</providers>
</membership>

      

Service behavior is configured to use MembershipProvider for use with username authentication.

        
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider"             membershipProviderName="MySqlMembershipProvider" />

      

The service behavior is configured to publish metadata.

        
<serviceMetadata httpGetEnabled="true" />

      

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.

        
<serviceCertificate 
   findValue="CN=machine.domain.com" />

      

WCF service—authorization

The service behavior is configured to use AspNetRoles with SqlRoleProvider.

        
<serviceAuthorization principalPermissionMode="UseAspNetRoles"
        roleProviderName="MySqlRoleProvider" />

      

WCF operations are configured to declaratively perform role checks at the operation level.

Declarative role checks on operations is the preferred mechanism.

        
[PrincipalPermission(SecurityAction.Demand, Role="Managers")]
public string GetData(int value)
{
 return string.Format("You entered: {0}", value);
}

      

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.

        
If(Roles.IsUserInRole(“Manager”))
{
   // do something for the manager
}
else
{
   // throw an error.
}

      

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.

        
SqlConnection sqlcon = new SqlConnection("Server=10.3.19.11;Database=Northwind;IntegratedSecurity=SSPI");

      

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.

        
exec sp_grantlogin 'Custom Service Account'

      

The login is mapped to a database user for the Web application.

This grants access to the specified database.

        
use targetDatabase 
go 
exec sp_grantdbaccess ' Custom Service Account' 
go

      

A database role is created in the target database.

This allows access control and authorization to the database.

        
use targetDatabase 
go 
exec sp_addrole 'DB Role Name' 
go 

      

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.

        
use targetDatabase
go
exec sp_addrolemember 'DB Role Name', 'Custom Service Account'
go

      

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