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

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

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 SQL Server data store. The basic model for this application scenario is shown in the following figure.

ch15scenario.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

ch15solution.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 IIS to host the service.
  • Host WCF in IIS.

Note: Due to a limitation with tables on this site, the code in the tables below may be run together when in the same cell. If this is a problem for you, please comment the workitem at
http://www.codeplex.com/CodePlex/WorkItem/View.aspx?WorkItemId=17004

Thick Client

Checks Example
WCF Proxy
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();
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. A proxy will invoke a WCF method within the service contained on the application server using the Service Accounts security context. myService.ClientCredentials.UserName.UserName = "username"; myService.ClientCredentials.UserName.Password = "p@ssw0rd"; myService.GetData(123);

Application Server

Checks Example
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.
IIS - Authentication
The IIS virtual directory is configured to use Anonymous access.






Checks Example
WCF Service - Configuration
ASP.NET database is created for use with SQL Membership Provider and SQL Role provider. Aspnetregsql.exe creates the SQL database to store the user and role information. aspnetregsql -S .\SQLExpress -E -A r m
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 use with Membership. 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>
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>
WCF Service process identity is given access permissions on 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 spgrantlogin '<<Custom Service Account>>' -- Grant the login access to the membership database USE aspnetdb GO spgrantdbaccess '<<Custom Service Account>>', '<<Custom Service Account>>' -- Add user to database role USE aspnetdb GO spaddrolemember 'aspnetMembershipFullAccess', '<<Custom Service Account>>' spaddrolemember 'aspnetRolesFullAccess', '<<Custom Service Account >>’
WCF Service is configured to use wsHttpBinding binding. 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. <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 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. This is required for protecting the user credentials in the message. <serviceCertificate findValue="CN=machine.domain.com" />
WCF Service - 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. 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 do programmatic roles checks, for fine grained access control. 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. 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 using the WCF process identity’s security context. Service does not impersonate the original caller to benefit for connection pooling.

Database Server

Check 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 DB. 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 Check
App Server to Database Server You can use IPSec or SSL between the 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 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

  • As the users are coming from the Internet and you cannot assume they have 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 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 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 lets you give specific minimum permissions to the role. In this way, if the database account changes you don't have to change the permissions on all database objects.

Communication Security

  • Message security protects sensitive data between the Thick Client and 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 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 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 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$' 

Additional Resources**

Last edited Jun 26, 2008 at 11:46 PM by rboucher, version 7

Comments

No comments yet.