This project is read-only.

Internet – Web to Remote WCF Using Transport Security (Trusted Subsystem)

- 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 web client to connect over the Internet to an ASP.NET application on an IIS server. 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.

Scenario5.jpg

Key Characteristics

This scenario applies to you if:
  • Your users have web clients
  • Your user accounts are stored in SQL
  • Your user roles are stored in SQL
  • The business logic behind your WCF service does not require fine-grained authorization
  • Your application transmits sensitive data over the network that needs to be protected
  • A high performance connection between the ASP.NET application and the WCF service is more important than the ability to host the WCF service in IIS

Solution

Solution5.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 WCF from the ASP.NET application
  • Use a service account to call the SQL Server from WCF
  • Use SSL to protect sensitive data between the web client and IIS
  • Use Transport security to protect sensitive data between the ASP.NET application and the WCF service
  • Use netTcpBinding to support the TCP transport for improved performance
  • Host WCF in a Windows Service since IIS does not support the TCP transport

Web 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 web application is configured to run under the service account. Assign the web application to the custom application pool.
Authentication The IIS virtual directory is configured to use Anonymous access. Users will be allowed to access pages and if required will be redirected to forms authentication page.
ASP.NET
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.
Web Application process identity is given access permissions on the ASPNET database. spgrantlogin 'Custom Domain Service Account' USE aspnetdb GO spgrantdbaccess 'Custom Domain Service Account', 'Custom Service' USE aspnetdb GO spaddrolemember 'aspnetMembershipFullAccess', 'Custom Service' spaddrolemember 'aspnetRolesFullAccess', 'Custom Service' Your Web Application process identity requires access to the Aspnetdb database. If you host the Web Application in Internet Information Services (IIS) 6.0 on Microsoft Windows Server® 2003, the NT AUTHORITY\Network Service account is used by default to run the Web Application.
Authentication ASP.NET is configured for Forms authentication <authentication mode = "Forms" > The web application will authenticate the users.
ASP.NET application is configured to deny access to all unauthenticated users <authorization> <deny users="?"/> <allow users="*"/> </authorization> Only authenticated users will be able to access the application
SqlMembershipProvider is configured to use with Membership feature for forms authentication <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. The membership feature also automatically creates the authentication ticket for you.
Authorization 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 code. Additionally, the role providers offer a consistent way for you to check the role membership of your users, regardless of the underlying data store.
Role-checks are performed using role manager APIs if (User.IsInRole("Role")) { //business operation }
WCF Proxy |
ASP.NET 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.
Proxy invokes services with the security context of service account myService.GetData(123); The proxy will automatically invoke WCF operations using the security context of the service account.
Caller Identity For auditing purposes, the identity of the caller can be passed in custom message headers during Proxy call. Additionally custom headers can be defined in message contracts or service contracts if (User.IsInRole("accounting")) { WCFTestService.MyServiceClient proxy = new WCFTestService.MyServiceClient(); using (OperationContextScope scope = new OperationContextScope(proxy.InnerChannel)) { string identity = User.Identity.Name; MessageHeader<string> headerIdentity = new MessageHeader<string>(identity); MessageHeader untypedMessageHeader = headerIdentity.GetUntypedHeader("identity", "ns"); OperationContext.Current.OutgoingMessageHeaders.Add(untypedMessageHeader); proxy.GetData("data"); } proxy.Close(); Use transport security to protect against spoofing attacks

Application Server

What Checks Example More Info
Windows Service
Configuration Windows Service is configured to run under a custom domain service account Use a domain account if possible.
WCF service is hosted in a Windows Service. Since IIS does not support netTcpBinding, host in Windows Service
WCF Service
Configuration Configure the WCF service to use netTcpBinding. <endpoint address="" binding="netTcpBinding" bindingConfiguration="" name="TcpBinding" contract="WCFServicecHost.IMyService" /> The NetTcpBinding uses the TCP protocol and provides full support for SOAP security, transactions, and reliability. As client and WCF service both are in intranet this is a good choice from performance perspective.
A mex endpoint is created for publishing the metadata <endpoint address="Mex" binding="mexTcpBinding" bindingConfiguration="" name="MexEndpoint" contract="IMetadataExchange" /> This is required so that client can add reference to the WCF Service using SvcUtil utility.
Service Metadata is configured in service behavior <serviceMetadata /> The service metadata entry is required for the windows service host to start. Both HTTP and HTTPS get are disabled
Authentication The netTcpBinding is configured to use Windows Authentication and Transport security. <endpoint address="" binding="netTcpBinding" bindingConfiguration=" " By default netTcpBinding is configured to use Windows Authentication and Transport Security.
Caller Identity Service retrieves the identity of the caller from the operationcontext For auditing purposes string identity = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("identity", "ns"); Use the identity to improve logging and auditing
SQL The connection string for database is configured to use windows authentication The database connection string includes Integrated Security=SSPI or Trusted Connection=Yes 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
Browser to Web Server SSL is used between browser and Web server to protect sensitive data on the wire. Install certificate in the Web site. Configure the virtual directory of the web application to use SSL
App server to Database IPSec or SSL can be used between App server and database server to protect sensitive data on the wire.

Analysis

Web Server

Authentication
  • To allow unauthenticated and unauthorized users to access pages and redirect to the login page, anonymous access in IIS is enabled.
  • Forms authentication is a good choice for this scenario because users come from the internet and have accounts in SQL.
  • The membership feature is a good choice to use with forms authentication, as it allows user authentication without writing and maintaining custom code.
Authorization
  • URL authorization is used to perform role checks against the original caller and restrict access to pages based on role permissions.
  • All authorization checks are performed in the web application before calls are made to the WCF service. The WCF service trusts the web application to perform this authorization and does not need to make fine-grained authorization decisions of its own.
  • The Roles Manager is a good choice for this scenario because it allows the application to look up users' roles without writing and maintaining custom code.
WCF Proxy
  • Because we are taking care of all authentication and authorization in the ASP.NET application, all calls through the WCF proxy and into the WCF service are made using the ASP.NET process identity’s security context.
  • If you need to produce audit logs showing what service operations were called by each user you can pass the identity of the original caller in a custom header.
Configuration
  • In order to reduce attack surface and minimize the impact of a compromise, the ASP.NET application on the Web Server is running under the security context of the Service account using a least privileged account.

Application Server

Authentication
  • WCF is configured to use Windows Authentication, in order to authenticate the ASP.NET service when it makes calls on the WCF Service.
Authorization
  • Since the WCF service trusts the ASP.NET application to authorize the user, no authorization is done in the WCF service.
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
  • This scenario is optimized around transmission performance at the expense of interoperability with clients that expect a legacy web service and the ability to host the service in IIS. For this reason the best binding choice is netTcpBinding. By default, netTcpBinding supports Windows Authentication with Transport security.
  • Because netTcpBinding is not supported by IIS 6.0, the WCF service is hosted in a windows service,
  • In order to reduce attack surface and minimize the impact of a compromise, the Windows service is running under the security context of the Service account using a least privileged account.
  • A metadata exchange (mex) endpoint is exposed to make it possible for the client to generate a proxy based on the service definition.

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

  • SSL is used between browser and Web server to protect sensitive data on the wire.
  • Transport security is used to protect sensitive data between Web Server and App Server.
  • IPSec or SSL can be used between App server and database server to protect sensitive data on the wire.

Example

Web Server

Code
  • Form is created to perform Forms authentication
  • Role-authorization occurs before WCF service invocation
  • ASP.NET calls WCF service if it is authorized
  • Identity of the original caller is retrieved from the User ticket context
  • Message Header containing the caller identity is created and passed to the operation context for auditing purposes

Form to do forms authentication
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
    </div>
    <asp:Login ID="Login1"  runat="server">
    </asp:Login>
    <asp:CreateUserWizard ID="CreateUserWizard1" runat="server">
        <WizardSteps>
            <asp:CreateUserWizardStep runat="server" />
            <asp:CompleteWizardStep runat="server" />
        </WizardSteps>
    </asp:CreateUserWizard>
    </form>
</body>
</html>
  • Proxy call invocation*
using System.ServiceModel;
using System.ServiceModel.Channels;
…
protected void Button1_Click(object sender, EventArgs e)
 {
*  		if (User.IsInRole("accounting"))*
        	{
            WCFTestService.MyServiceClient proxy = new WCFTestService.MyServiceClient();
            using (OperationContextScope scope = new OperationContextScope(proxy.InnerChannel))
            {
                string identity = User.Identity.Name;
*                MessageHeader<string> headerIdentity = new MessageHeader<string>(identity);*
*                MessageHeader untypedMessageHeader = headerIdentity.GetUntypedHeader("identity", "ns");*
*                OperationContext.Current.OutgoingMessageHeaders.Add(untypedMessageHeader);*
                proxy.GetData("data");
            }
      proxy.Close();
      }
 }



Configuration
  • Windows and anonymous authentication are enabled
  • Connection string to the SqlMembershipProvider and to the SqlRoleProvider are configured
  • SQLmembershipProvider is enabled
  • Only authenticated users are allowed to browse the site
  • Role Manager is enabled
       <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>
*		<authentication mode="Forms"/>*
		<authorization>
*			<deny users="?"/>*
*			<allow users="*"/>*
		</authorization>
	

		<pages>
			<controls>
				<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
                        PublicKeyToken=31BF3856AD364E35"/>
				<add tagPrefix="asp" namespace="System.Web.UI.WebControls" 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>

Application Server

Code
  • The service retrieves the identity of the caller from the operation context if it is required for auditing purposes
  • The service calls SQL using the security context of the WCF service
using System.Data.SqlClient;


  public string GetData(string myValue)
  {
 
      SqlConnection sqlcon = new SqlConnection("Server=SqlServer;Database=testdb;Integrated Security=SSPI");
*      sqlcon.Open();*
	//do the business operation	
      *string identity = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("identity", "ns");*
      return “some data” ;

   }

Configuration
  • The service has a binding endpoint that uses netTcpbinding with the default settings
  • The service has a service behavior configuration to publish metadata
  • The service has a base address configured
  • The service behavior is configured with element serviceMedata to allow metadata exposure
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="BehaviorConfiguration">
                    *<serviceMetadata />*
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <bindings />
        <services>
            <service behaviorConfiguration="BehaviorConfiguration" name="WCFServicecHost.MyService">
*                <endpoint address="Mex" binding="mexTcpBinding" bindingConfiguration=""*
*                    name="MexEndpoint" contract="IMetadataExchange" />*
*                <endpoint address="" binding="netTcpBinding" bindingConfiguration=""*
*                    name="TcpBinding" contract="WCFServicecHost.IMyService" />*
                <host>
                    <baseAddresses>
*                        <add baseAddress="net.tcp://perfpres02.npscode.com/MyService" />*
                    </baseAddresses>
                </host>
            </service>
        </services>
    </system.serviceModel>
 

Database Server

Configuration**
  • A SQL server login is created for the WCF service account
  • The WCF login name is given access to the database
  • The role is created in the 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\aspnethost' 

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 9, 2008 at 9:13 PM by prashantbansode, version 3

Comments

colinbo Jun 12, 2009 at 3:59 AM 
With the web server accessing the database directly doesn't that defeat the purpose of having the n-tier model?