3

I'm new to WCF and I'm making server/client application in which I need to have some user/password schema to maintain some customization settings for each client and to log who access the service, but "security" in the traffic going through the net is not really needed, since information is not sensitive. So taking this into account I was searching for a SIMPLE way of accomplishing this but I couldn't find it.

I have a few constraints and conditions:

  • Windows security is not an option.
  • I'm using clickonce deployment, so everything should be contained within the installation packet. I don't know the actual list of users that are downloading it, so I don't have a way to distribute some certificate to all users.
  • Also the client will be accesed within the LAN and through several WANs. Another requirement has to be met is that the service should have very good performance since a lot of data is flowing with each response, so the questions is:

Does message security hurts performance notoriously?

The "manual" way would be to pass the username as a parameter for each method I'm exposing, but it seems like a very dirty solution.

It seems to me a lot of constrains to design this, so that's why I'm asking about this.

Which would be the simplest solution to accomplish this?

Tolga Evcimen
  • 7,112
  • 11
  • 58
  • 91
Nacho
  • 87
  • 7

1 Answers1

3

First of all we have to assume that all users consuming the service are in some way "registered" to use the service. Because if it is out in the open, anonymous, then there is simply no tracking. So my assumption is as follows:

  1. The service is hosted in Windows Service/WinForms to support TCP Endpoint. - With new versions of IIS(>6) this is not a required assumption anymore
  2. There is a combination like "UserName/Password" to authenticate. This is not in the active directory (not opting windows authentication) but may be xml/database.
  3. We are not willing to have methods like public int Add(string User, string Password, int A, int B)

I have a service with a TCP endpoint which does something like this. I will share that. I don't claim it is the best practice.

Application name is MYAPP

I have provided

customUserNamePasswordValidatorType="MYAPPHost.Authenticate, MYAPPHost" 

in

serviceCredentials > userNameAuthentication 

section of web.config.

MYAPPHost is name of my windows service. Authenticate is the class which does the authentication from Database.

message clientCredentialType="UserName" is set for TCPBinding.

App.Config of my windows service:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
          switchValue="Off" propagateActivity="true" >
        <listeners>
          <add name="SERVICE_MONITOR" type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="MYAPP_MONITOR.svclog" />
        </listeners>
      </source>      
      <source name="MYAPP_TRACE" switchValue="All" >
        <listeners>
          <add name="MYAPP_TRACE_LISTENER" type="System.Diagnostics.XmlWriterTraceListener"                                         
               initializeData="MYAPP_TRACE.svclog" />
        </listeners>
      </source>
    </sources>
    <trace autoflush="true" />
  </system.diagnostics> 

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="OverAllServiceBehavior">
          <serviceSecurityAudit 
            auditLogLocation="Application" 
            serviceAuthorizationAuditLevel="Failure" 
            messageAuthenticationAuditLevel="Failure" 
            suppressAuditFailure="true" />          
          <serviceDebug includeExceptionDetailInFaults="True" />
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />
          <serviceThrottling maxConcurrentCalls="10000" maxConcurrentSessions="10000">    
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
          <serviceCredentials>
            <userNameAuthentication 
              userNamePasswordValidationMode="Custom" 
              customUserNamePasswordValidatorType="MYAPPHost.Authenticate, MYAPPHost"/>
          </serviceCredentials>         
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="OverAllEndPointBehavior" />
      </endpointBehaviors>
    </behaviors>        
    <bindings>
      <netTcpBinding>
        <binding name="ServiceTCPEndPointBinding" maxBufferSize="2147483647">    
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxNameTableCharCount="2147483647" />
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="None" />
            <message clientCredentialType="UserName" algorithmSuite="TripleDes"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>        
    <services>
      <service behaviorConfiguration="OverAllServiceBehavior"
               name="MiddleWare.ServiceClasses.ServiceClass">    
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://127.0.0.1:15010/ServiceTCPEndPointMEX"/>            
          </baseAddresses>
        </host>    
        <endpoint address="net.tcp://127.0.0.1:15020/ServiceTCPEndPoint" contract="MiddleWare.ServiceContracts.IServiceContract" />           
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />    
      </service>
    </services>
  </system.serviceModel>
</configuration>

Authenticate Class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Selectors;

namespace MYAPPHost
{
    public class Authenticate : UserNamePasswordValidator
    {
        public override void Validate(string UserName, string Password)
        {
            if (!CheckFromDB(UserName,Password))
                throw new Exception("UNAUTHORIZED ACCESS!!!");
        }
    }
}

In Client Side,after adding reference to the WCF (SR)

SR.ServiceContractClient obj = new SR.ServiceContractClient("ServiceTCPEndPoint");
obj.ClientCredentials.UserName.UserName = "User1";
obj.ClientCredentials.UserName.Password = "Password1";
int I = obj.Add(1, 2);

If credentials are not provided, message security token error is thrown. For wrong credentials UNAUTHORIZED ACCESS occurs.

Tolga Evcimen
  • 7,112
  • 11
  • 58
  • 91