Internet – Windows Forms Client Calling WCF Using Message Security

- J.D. Meier, Jason Taylor, Prashant Bansode, Carlos Farre, Madhu Sundararajan, Steve Gregersen

Applies To

  • WCF 3.5

Scenario

In this scenario, your users do not have 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 SQL Server data store. The basic model for this application scenario is shown in the following figure.

Scenario7.jpg

Key Characteristics

This scenario applies to you if:
  • Your users have Windows Forms clients
  • Your user accounts are stored in SQL
  • Your user roles are stored in SQL
  • Your application transmits sensitive data over the network that needs to be protected
  • The ability to host the WCF service in IIS is more important than a high performance connection between the ASP.NET application and the WCF service

Solution

Solution7.jpg

Solution Summary Table

In this solution you will:
  • Use 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 the service to be hosted in IIS
  • Host WCF in IIS

Thick Client

||What ||Checks ||Example ||More Info||
WCF Proxy
Thick Client has a proxy reference to the WCF service. WCFTestService.ServiceClient myService = new WCFTestService.ServiceClient(); The application has access to the WCF metadata to create a service reference.
Root 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 myService.ClientCredentials.UserName.UserName = "username"; myService.ClientCredentials.UserName.Password = "p@ssw0rd"; myService.GetData(123); A proxy will invoke a WCF method within the service contained on the application server using the Service Accounts security context.


Application Server

What Checks Example More Info
IIS
Configuration 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.
Authentication The IIS virtual directory is configured to use Anonymous access.
WCF Service
Configuration Aspnet database is created to be used with SQL Membership Provider and SQL Role provider. aspnetregsql -S .\SQLExpress -E -A r m Aspnetregsql.exe creates the sql database to store the user and role information.
Connection string configured to point to the user and role store in SQL Server. <add name="MyLocalSQLServer" connectionString="Initial Catalog=aspnetdb;data source=localhost;Integrated Security=SSPI;" /> The database connection string includes Integrated Security=SSPI or Trusted Connection=Yes for Windows authentication.
SqlMembershipProvider is configured to use with Membership <membership defaultProvider="MySqlMembershipProvider"> <providers> <clear/> <add name="MySqlMembershipProvider" connectionStringName="MyLocalSQLServer" applicationName="MyAppName" type="System.Web.Security.SqlMembershipProvider"/> </providers> </membership> The membership feature helps protect credentials, can enforce strong passwords, and provides consistent APIs for user validation and secure user management.
Role Manager feature is enabled and SqlRoleProvider is configured for roles authorization. <roleManager enabled="true" defaultProvider="MySqlRoleProvider" > <providers> <clear/> <add name="MySqlRoleProvider" connectionStringName="MyLocalSQLServer" applicationName="MyAppName" type="System.Web.Security.SqlRoleProvider" /> </providers> </roleManager> Role manager feature allows you to look up users' roles without writing and maintaining custom code.
WCF Service process identity is given access permissions on the ASPNET database. spgrantlogin '<<Custom Service Account>>' USE aspnetdb GO spgrantdbaccess '<<Custom Service Account>>', '<<Custom Service Account>>' USE aspnetdb GO spaddrolemember 'aspnetMembershipFullAccess', '<<Custom Service Account>>' spaddrolemember 'aspnetRolesFullAccess', '<<Custom Service Account >>’ Your WCF service process identity requires access to the Aspnetdb database.
WCF Service is configured to use wsHttpBinding binding <endpoint address="" binding="wsHttpBinding" bindingConfiguration="BindingConfiguration" name="WsBinding" contract="IService"/> The wsHttpBinding uses the HTTP protocol and provides full support for SOAP security, transactions, and reliability. As clients are in the internet this is the only choice.
Authentication The 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 <membership defaultProvider="MySqlMembershipProvider"> <providers> <clear/> <add name="MySqlMembershipProvider" connectionStringName="MyLocalSQLServer" applicationName="MyAppName" type="System.Web.Security.SqlMembershipProvider"/> </providers> </membership> The membership feature automatically authenticates and creates the authentication ticket for you.
Service behavior is configured to use membership provider for using with username authentication. <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="MySqlMembershipProvider" /> |
Service behavior is configured to publish metadata <serviceMetadata httpGetEnabled="true" />
Service certificate is installed on the WCF Service machine. The service behavior is configured to use the service certificate. <serviceCertificate findValue="CN=machine.domain.com" /> This is required for protecting the user credentials in the message.
Authorization Service behavior is configured to use AspNetRoles with SqlRoleProvider <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="MySqlRoleProvider" />
WCF Operations are configured to do role checks at operation level, declaratively. [PrincipalPermission(SecurityAction.Demand, Role="Managers")] public string GetData(int value) { return string.Format("You entered: {0}", value); } Declarative role-checks on operations is the preferred mechanism.
Roles APIs is used to do programmatic roles checks, for fine grained access control. If(Roles.IsUserInRole(“Manager”)) { // do something for the manager } else { // throw an error. } If you need finer-grained authorization control, you can use imperative role checks in the code itself. Use call to Roles.IsUserInRole to perform the check.
SQL The connection string for database is configured to use windows authentication SqlConnection sqlcon = new SqlConnection("Server=10.3.19.11;Database=Northwind;IntegratedSecurity=SSPI"); The database connection string includes Integrated Security=SSPI or Trusted Connection=Yes.
Database connection is opened using the WCF process identity’s security context. Service does not impersonate the original caller to benefit for connection pooling.

Database Server

What Check Example More Info
Configuration A SQL Server login is created for the WCF’s service account (process identity). exec sp_grantlogin 'Custom Service Account' This grants access to the SQL Server
The login is mapped to a database user for the Web application. use targetDatabase go exec sp_grantdbaccess ' Custom Service Account' go This grants access to the specified database.
A database role is created in the target database. use targetDatabase go exec sp_addrole 'DB Role Name' go This allows access control and authorization to the DB.
The login is added to the database role. use targetDatabase go exec sp_addrolemember 'DB Role Name', 'Custom Service Account' go 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 Check Example More Info
App server to Database IPSec or SSL can be used between App server and database server to protect sensitive data on the wire.


Analysis

Thick Client

WCF Proxy
  • Because 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 is 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
  • As the users are coming from the Internet and we cannot assume a Windows account, the user information is stored in SQL. 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 user name 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 in the WCF Service at the operation level, declaratively.
  • For fine grained access control or implementing business logic, authorization checks are made within the operations programmatically.
  • The Roles Manager is a good choice for this scenario because it allows you to look up users' roles without writing and maintaining custom code.
SQL
  • To reduce the chances of database credentials being stolen, the database connection string is configured to use Windows authentication. This choice avoids storing credentials in files and passing 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 to be used.
Configuration
  • Since all of the client 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, we hosted the WCF service in IIS.
  • In order to reduce 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 to SQL server application roles to avoid the associated password management and connection pooling issues associated with the use of SQL application roles. Applications activate SQL 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 application roles, which severely impacts application scalability.
  • Creating a new user-defined database role and adding the database user to the role, allows you give specific minimum permissions to the role, so that if the database account changes; you don't have to change the permissions on all database objects.

Communication Security

  • Message security is used to protect sensitive data between Thick Client and WCF Service.
  • IPSec or SSL can be used between WCF Service and 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 using windows authentication.
using System.Data.SqlClient;
using System.Web.Security;


public string GetData(string myValue)
{
           
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 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 provider for authentication.
  • The service has a service behavior to use the SqlMemberShipProvider.
  • The service behavior is configured with the element serviceAuthorization to allow UseAspNetRoles as the authorization provider.
  • The service behavior is configured with the element serviceMetadata to allow publishing 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

  • 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$' 

Contributors and Reviewers

  • External Contributors and Reviewers:
  • Microsoft Consulting Services and PSS Contributors and Reviewers:
  • Test team: Rohit Sharma, Chaitanya Bijwe, Parameswaran Vaideeswaran.
  • Edit team: Dennis Rea.
  • SEO team: Rob Boucher.

Last edited Apr 10, 2008 at 5:40 PM by prashantbansode, version 3

Comments

vontlin May 3, 2010 at 6:03 AM 
how can we make the application name dynamic, i mean if the client to pass the application name in the membership provider

testman Sep 24, 2008 at 7:22 AM 
Thanks. This was what I searched for days~~

cata02 Apr 4, 2008 at 7:04 AM 
Why not use HTTPS between the WinForms client and the WCF service?