Click here to Skip to main content
12,448,164 members (60,149 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

5.8K views
2 bookmarked
Posted

BasicHttpBinding with Custom Authorization

, 24 Feb 2015 CPOL
Rate this:
Please Sign up or sign in to vote.
Monitor Network Traffic for BasicHttpBinding WCF call using Wireshark

Introduction

This article demonstrates the network traffic passed between 2 computers when a WCF client calls a WCF service while passing a username/password. I used Wireshark to monitor the network traffic.

Note this is for demonstration purposes. Using BasicHttpBinding like this is NOT secure.

Background

WCF has a myriad of permutations of configurations e.g. bindings, security. The number of permutations can seem overwhelming. This article shows what is happening under the hood when a simple service call is made using BasicHttpBinding.

Using the code

There are 2 projects - one for the WCF service and one for the WCF client.

I connected my 2 PC's together using a crossover cable. I then set up each PC with an IP address. I assigned IP address 192.168.1.10 to the PC hosting the WCF service as below.

For the client, I assigned the IP address 192.168.1.11

WCF Service

Here is the code for the WCF service. Note the use of the CustomValidator class. I am going to get the WCF client to transmit a username and password to the WCF service and authentication will fail if the password is not = "Secret". Beyond that the code is fairly standard service code but specifies that the CustomValidator is doing the authentication.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.IdentityModel.Selectors;

namespace JoesTestWCFService
{
    public class CustomValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (password != "Secret")
                throw new FaultException("Bad Password " + password);

            return;
        }
    }

    [ServiceContract]
    public interface IJoesService
    {
        [OperationContract]
        string TestMethod(string msg);
    }

    public class JoesService : IJoesService
    {
        Guid serviceId;
        public JoesService()
        {
            serviceId = Guid.NewGuid();
        }
        public string TestMethod(string msg)
        {
            return "Hello " + msg + ": (" + serviceId.ToString() + ")";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Uri[] baseAddress = null;

            baseAddress = new Uri[] { new Uri("http://192.168.1.10:8082/") };
            
            var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);

            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

            ServiceHost host = new ServiceHost(typeof(JoesService), baseAddress);

            host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
            host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomValidator();

            host.AddServiceEndpoint(typeof(IJoesService), binding, baseAddress.First());

            host.Open();

            Console.WriteLine("Ready ...");
            Console.ReadLine();
        }
    }
}

WCF Client

Here is the code for the WCF client. Note I pass a username/password when calling the WCF service.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace JoesTestWCFClient
{
    [ServiceContract]
    public interface IJoesService
    {
        [OperationContract]
        string TestMethod(string msg);
    }

    class Program
    {
        static void Main(string[] args)
        {
            string uri = null;
            EndpointAddress endPA;

            uri = "http://192.168.1.10:8082";
            var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

            endPA = new EndpointAddress(uri);
            ChannelFactory<IJoesService> factory = new ChannelFactory<IJoesService>(binding, endPA);

            factory.Credentials.UserName.UserName = "JJJOE";
            factory.Credentials.UserName.Password = args[0];

            IJoesService proxy = factory.CreateChannel();

            string input;
            do
            {
                Console.WriteLine(proxy.TestMethod("JOEJOEJOEJOEJOE"));

                input = Console.ReadLine();
            } while (input != "x");
        }
    }
}

Network Traffic

Here is all the traffic for one call from the WCF client to the WCF service

1) The first 3 lines are just setting up the TCP connection. Note that although Http is seen as "connectionless", under the hood a TCP connection is established for the duration of the call.

Note the port (49569) for the client. This wasn't assigned by me. The destination port (8082) was though.

2) In the next line, the WCF client makes a request on the WCF service i.e. TestMethod on IJoesService on host 192.168.1.10 - port 8082

4) The next line (41) is a Challenge by the WCF Service because request was unauthenticated.

see http://www.ietf.org/rfc/rfc2617.txt?number=2617

The "basic" authentication scheme is based on the model that the client must authenticate itself with a user-ID and a password for each realm.  The realm value should be considered an opaque string which can only be compared for equality with other realms on that server. The server will service the request only if it can validate the user-ID and password for the protection space of the Request-URI.

   There are no optional authentication parameters.

   For Basic, the framework above is utilized as follows:

      challenge   = "Basic" realm

      credentials = "Basic" basic-credentials

   Upon receipt of an unauthorized request for a URI within the protection space, the origin server MAY respond with a challenge like the following:

      WWW-Authenticate: Basic realm="WallyWorld"

so here is the challenge from my WCF service

WWW-Authenticate: Basic realm="".

5) In the line (42), the WCF client responds to the Challenge by the WCF service by passing Credentials.

To receive authorization, the client sends the userid and password,
   separated by a single colon (":") character, within a base64 [7]
   encoded string in the credentials.
 
      basic-credentials = base64-user-pass
      base64-user-pass  = <base64 [4] encoding of user-pass,

If the user agent wishes to send the userid "Aladdin" and password
   "open sesame", it would use the following header field:
 
      Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

Note the credentials are NOT encypted - they are encoded. It is straightforward to extract the password as I show next.

You can see from Wireshark that the client has passed

Authorization: Basic SkpKT0U6U2VjcmV0

You can use the site such as http://string-functions.com/base64decode.aspx to decode this.

6) Next the WCF Service grants permission to continue.

7) The WCF client can now call the service method.

8) The WCF service responds

Points of Interest

1) Obviously this method is not secure as it is straightforward to decode the username and password if it is intercepted.

A more secure method to pass the username and password would use a more secure binding e.g. netTcpBinding. This is a little more complex to set up. One method I have used is to set up a self certified certificate on the WCF service PC. The public key of this certificate is sent from the WCF service to the WCF client so that the client can use it to encrypt credentials.

Below is a snapshot of Wireshark traffic where I have used netTcpBinding. You can see the WCF service passing the public key of it's certificate to client (I've circled the start of the public key in the traffic - you can see it matches the Certificate key in the screen to the right)

2) Repeated calls to the BasicHttpBinding version of the code above is PerCall by default i.e. there is a fresh instance of JoesService called each time TestMethod is called. You will see a different ServiceId printed on each method call.

If you want a Per-session for your client you will need to use another binding such as netTcpBinding.

 

All the above is for demo purposes only, to hopefully help demystify what happens under the hood when WCF makes calls between PCs. Please do your own research to establish the appropriate security model you need in your own projects.

History

v1.0 Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Joe Murphy
United Kingdom United Kingdom
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionAre other bindings really an option? Pin
dommy1A25-Feb-15 2:16
memberdommy1A25-Feb-15 2:16 
AnswerRe: Are other bindings really an option? Pin
Joe Murphy25-Feb-15 9:00
memberJoe Murphy25-Feb-15 9:00 
GeneralRe: Are other bindings really an option? Pin
dommy1A25-Feb-15 23:11
memberdommy1A25-Feb-15 23:11 
GeneralRe: Are other bindings really an option? Pin
Joe Murphy26-Feb-15 8:20
memberJoe Murphy26-Feb-15 8:20 
GeneralRe: Are other bindings really an option? Pin
dommy1A26-Feb-15 8:32
memberdommy1A26-Feb-15 8:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160811.3 | Last Updated 24 Feb 2015
Article Copyright 2015 by Joe Murphy
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid