Pages

Sunday, March 13, 2016

Breaking All The Rules with WCF Full Circle


Recently while working on a project for a large government enterprise, I encountered a problem similar, nay, exactly the same as Scott Hanselman in his post "Breaking All The Rules with WCF." From the malformed WSDL to a custom WS-Security usernameToken without a password, I was dealing with the same issue.

Scott, as always, has done an incredible job highlighting the problem, and providing a solution, so I highly encourage you to read his post. I'm writing this post to cover the one thing he missed, creating a service that can be used to test this binding configuration against.

CREATING THE BINDING

Scott was able to create a Custom WCF binding that emits a username without password using the following code:

WSHttpBinding oldBinding = new WSHttpBinding();
oldBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
//Just the username
oldBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
//And basically nothing else
oldBinding.Security.Message.NegotiateServiceCredential = false;
oldBinding.Security.Message.EstablishSecurityContext = false;

//oldBinding.ProxyAddress = new Uri("http://BIGASSLAPTOP:8888");
//oldBinding.UseDefaultWebProxy = false;

//remove the timestamp
BindingElementCollection elements = oldBinding.CreateBindingElements();
elements.Find().IncludeTimestamp = false;

//sets the content type to application/soap+xml
elements.Find().MessageVersion = MessageVersion.Soap12;
CustomBinding newBinding = new CustomBinding(elements);
FooPortTypeClient svc = new FooPortTypeClient(newBinding, new EndpointAddress("https://example.com/foo/v1"));
FooRequest req = new FooRequest();
//...etc...now it's just request and response.

The same binding can be created in configuration like this:

<bindings>
 <customBinding>
  <binding name="WSSecurityBinding">
  <security defaultAlgorithmSuite="Default"
     authenticationMode="UserNameOverTransport"
     requireDerivedKeys="true"
     includeTimestamp="false"
     messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
   <localClientSettings detectReplays="false" />
   <localServiceSettings detectReplays="false" />
  </security>
  <textMessageEncoding messageVersion="Soap12" />
  <httpsTransport />
  </binding>
 </customBinding>
</bindings>

This will create the binding, but what about testing it?

TESTING

If you configure a test client and service with this binding you'll find that as soon as the client calls the service, you'll receive the following exception:


This exception is thrown because the default UserNamePasswordValidator expects both a username and a password... and we just removed the password. However, fixing this issue is easy enough... we can create a custom UserNamePasswordValidator that allows us to validate the request according to our custom business logic.

public class NullPasswordValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        if (string.IsNullOrWhiteSpace(userName))
            throw new FaultException("The userName cannot be null or whitespace.");

        // Allow any password, even null values to pass.
    }
}

Configuring the service to use this new validator is as simple as creating a new service behavior configuration and pointing to the new custom validator:

  <serviceBehaviors>
    <behavior name="NullPasswordValidator">
      <serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom"
                                customUserNamePasswordValidatorType="Security.WCF.Validators.NullPasswordValidator, Security" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>

FULL CIRCLE

After creating and configuring the new UserNamePasswordValidator, our client will be able to communicate with the service. We can open up Fiddler and prove that everything is working correctly by inspecting the SOAP message:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
 <s:Header>
  <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <o:UsernameToken u:Id="uuid-38dc2ff9-a055-4a66-90e3-3aa0c267c36e-1">
    <o:Username>viaMacchina\\dec</o:Username>
   </o:UsernameToken>
  </o:Security>
 </s:Header>
 <s:Body>
  ...
 </s:Body>
</s:Envelope>

This brings the custom binding full circle. We're able to create a message request without a password to fulfill the custom WS-Security requirements. We're also able to create a test service that will accept the requests without throwing an exception. Finally, we can use tools like Fiddler to inspect the SOAP messages between the client and service to ensure that the message is formed exactly as it should be.

4 comments:

  1. I’m impressed with the post which you have shared. This is very informative to know the benefits of outsourcing web development services.
    Woocommerce Development Services
    Wordpress Customization Service
    hire wordpress programmers
    Convert Website to Wordpress
    hire wordpress developers

    ReplyDelete
    Replies
    1. Great Article
      Cyber Security Projects

      projects for cse

      Networking Security Projects

      JavaScript Training in Chennai

      JavaScript Training in Chennai

      The Angular Training covers a wide range of topics including Components, Angular Directives, Angular Services, Pipes, security fundamentals, Routing, and Angular programmability. The new Angular TRaining will lay the foundation you need to specialise in Single Page Application developer. Angular Training

      Delete