Introduction
This is part two in a multi-part blog series detailing my experience building and testing WCF applications. If you haven’t read it yet, I recommend checking out part one of the series, as it provides context for the rest of the posts in this series.WsHttpBinding
In “My WCF Experience Part 1”, I built an out-of-the-box basicHttpBinding WCF service, which exposed me to the concept of “service oriented architecture” (SOA). Yet, basicHttpBinding is not capable of using features like WS-Security, and so it is likely most assessments I’ll do won’t use basicHttpBinding. To progress my understanding of WCF further, I wanted to learn WCF services the way Security PS’s clients were implementing them. At the direction of my mentor I decided my next step was to try my hand at defining a service that employed wsHttpBinding.WsHttpBinding implements a binding over HTTP that supports Web Services (WS) protocols and standards. This includes many of the security mechanisms built into the WS standards. The default security standard implemented by wsHttpBinding is message-level security. Message-level security provides the ability to encrypt transmitted messages, even if the application or service is using HTTP which itself is not encrypted. So in an environment where the underlying protocol may not be encrypted and is possibly susceptible to interception, the messages themselves are encrypted and not readable without the proper decryption mechanism. This provides message-level security even over non-TLS, HTTP connections.
Here is the binding contract I used to implement wsHttpBinding. We’ll discuss defining binding contracts later on, so don’t worry if it looks like foreign XML. For now, I’m providing it as the “solution up front” and we’ll dive more into the highlighted elements and why it’s in an XML format down below.
…
<bindings>
<wsHttpBinding >
<binding name="WSHttpBinding_IService1">
<security mode="Message" >
<message negotiateServiceCredential="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WcfServiceLibrary1.Service1">
<host>
<baseAddresses>
<add baseAddress="http://mywcfservice.local:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary1.IService1" bindingConfiguration="WSHttpBinding_IService1" >
<!-- Upon deployment, the following identity element should be removed or replaced to reflect the identity under which the deployed service runs. If removed, WCF will infer an appropriate identity automatically.
-->
<!--<identity>
<dns value="mywcfservice.local"/>
</identity>-->
</endpoint>
…
<bindings>
<wsHttpBinding >
<binding name="WSHttpBinding_IService1">
<security mode="Message" >
<message negotiateServiceCredential="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WcfServiceLibrary1.Service1">
<host>
<baseAddresses>
<add baseAddress="http://mywcfservice.local:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary1.IService1" bindingConfiguration="WSHttpBinding_IService1" >
<!-- Upon deployment, the following identity element should be removed or replaced to reflect the identity under which the deployed service runs. If removed, WCF will infer an appropriate identity automatically.
-->
<!--<identity>
<dns value="mywcfservice.local"/>
</identity>-->
</endpoint>
…
Key elements for this binding are highlighted in green, and I will discuss why the
identity
property (in red, above) has been commented out. In this blog post, I want to discuss two lessons I learned in writing this contract: defining the security
attributes via the App.config
file and removing the identity
attributes to prevent token authentication errors.Defining Security Attributes Administratively vs. Programmatically
Establishing wsHttpBinding versus the basicHttpBinding was a lot trickier than I anticipated, because it was at this point that the documentation available online started to break down. Rather than a singular tutorial designed as a “one size fits all” approach, I found everything from general information on wsHttpBinding to extremely environment-specific issues that did not fully address the setup issues I encountered. It was supremely frustrating as I simply wanted to get the service implemented at the most basic level.wsHttpBinding has many of the standard WS-* security features already built-into its definition; all developers need to do is select which attributes to implement based on their specific deployment. In order to achieve the message-level encryption mentioned above, within your binding configuration, you must set your security mode like the following:
<security mode="Message" >
Curiously, once I started attempting to implement the
security
properties to define message-level encryption, I kept receiving errors like:- Visual Studios “token generation” errors
- Client “improper authentication” errors
- Setup mismatched configuration files
Eventually, I discovered my implementation differed from many others in that I opted to set my binding configuration administratively via the
App.config
file, whereas many others implemented their settings programmatically within the actual application. This is one of the great things about Visual Studio: users can choose to set their configuration settings either in a structured XML .config file or via programmatic statements in their code. It is an entirely personal choice depending on what the coder desired.Lesson learned #1: It is possible to define your settings both programmatically as well as administratively through a configuration file. Your best implementation may change depending on your project. Administrative configuration made more sense to me while learning this process. Be prepared to do your research no matter which route you opt to employ. More resources available here and here.
Authenticating WCF Services
While this was a good lesson learned, I still had authentication errors to contend with. Enter the role of authenticating services.WCF clients need to know and authenticate the service to which they are connecting. It is not enough for a service to claim they are an authentic, non-malicious source; clients must be able to trust the service is who it claims to be. If not, clients may be redirected to malicious services and so become victims of spoofing attacks. In order to achieve this security control and prevent said phishing attacks, WCF provides a way for services to authenticate themselves.
WCF provides the Identity property of the EndpointAddress class to achieve this functionality. This property represents the identity of the service to which the client is attempting to connect. The WCF infrastructure will automatically authenticate the service using this property prior to any code being executed by the service on the client. It authenticates the service using one of six different types of identities (as derived from this .NET developers document):
- DNS: X.509 certificate or Windows accounts
- Certificate: B64-encoded X.509 certificates
- Certificate Reference: Same as certificate, but you can store the certificate in different locations and only have to update the reference to it.
- RSA: RSA key value
- User Principal Name: Specifies that the service is running under a specific Windows user account (using Kerberos security if in an AD environment)
- Service Principal Name (SPN): Ensures the SPN and the specific Windows account associated with this SPN both identify the service
After much Googling, troubleshooting, tearing down and building back up again, I stumbled across a note hidden in an MSDN document which solved all of the token generation and improper authentication problems I experienced with wsHttpBinding. Because the
identity
property (which is included by default when adding a wsHttpBinding to a service) forces the underlying WCF infrastructure to validate the service’s identity before the client attempts to authenticate to it, my service has to be able to supply sufficient credentials matching the authentication type in order to work properly. In my development environment, I was creating a very simple client and service using a local instance of IIS Express, and so I did not have the infrastructure (or desire) to build in sufficient authentication mechanisms for my service. Thankfully, removing the identity
property altogether in the configuration file fixed the token authentication and allowed my binding to establish an HTTP connection using WS-* standards.As may be seen in the screenshot below, my new WCF service now encrypts the message body in both the request and response.
Raw Encrypted Request |
Raw Encrypted Response |
identity
property in development and allow your client to connect to your service. This is a development hack that worked great for me to get this proof of concept working, but this is not suitable (nor is it recommended) for production. If a WCF service is deployed into production without the identity
property enabled, clients may become susceptible to phishing attacks due to redirection to malicious services.
0 comments:
Post a Comment