Channels ▼

Arnon Rotem-Gal-Oz

Dr. Dobb's Bloggers

Implementing a WCF Service Firewall: Part II of N

March 04, 2009

the previous installment provided some context as to why I want to implement this pattern. This
installment will look at some of the implementation options.

As I noted before, WCF provides quite a lot of extension points on the route the message pass from arriving on the service to the point WCF
calls the actual method in the service instance. Several of those extension points are possible candidates for the Service Firewall for instance

  • Contract Filter-The contract filter is responsible to route messages to the appropriate contract. It needs to be a subclass of a MessageFilter. It looks that the contract filter is a good option since it intercepts the call rather early so it means it would probably be the fastest option. Also its name (filter..) implies it is a good option
  • Message Inspector - The Message inspector is responsible for looking at or modifying messages when they enter a service and looks like a natural candidate for the job. There are two kinds of Message Inspectors: Those who look at messages on the client side (implement the IClientMessageInspector interface) and those that look at the server side (implement the IDispatchMessageInspector). It seems that the latter is the type of inspector we need here.
  • Service Authorization Manager - responsible for evaluating policies, claims etc. of the client to make sure that a call is valid from the security perspective. This looks like it would be a good class to use for a real service firewall. It seems it won't be a good fit for the purpose of what we need here.

When I need to choose  between several technical options that seem to be similar I usually do a POC - proof of concept.  A piece of throwaway code to get a feel of the different options and better understand their strengths and weaknesses (in the context of the solution I seek).

What I did was to take a class I prepared for some of the integration tests of the EventBroker and build a few extensions that interact with them. Here is some of the setup code of the environment:

<span style="background-color: transparent; font-family: Courier New; color: black; font-size: 11px">testServer <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> Tester();  service1 <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> ServiceHost(testServer, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> Uri(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">string</span>.Format(<span style="background-color: #e4e4e4; font-family: Courier New; color: #666666; font-size: 11px">"http://localhost:{0}"</span>, TestServerPort)));  var binding <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> WebHttpBinding  {      ReaderQuotas <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> { MaxArrayLength <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> 600000 },      MaxReceivedMessageSize <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> 800000,      MaxBufferSize <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> 800000   };   var ep <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> service1.AddServiceEndpoint(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">typeof</span>(TestingContract), binding, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">string</span>.Format(<span style="background-color: #e4e4e4; font-family: Courier New; color: #666666; font-size: 11px">"http://localhost:{0}/S1"</span>, TestServerPort));  ep.Behaviors.Add(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> WebHttpBehavior()); <font color="#ff0000"> ep.Behaviors.Add(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> InspectorBehavior());  service1.Authorization.ServiceAuthorizationManager <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> TestAuthorizer();</font>  var cp <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> service1.AddServiceEndpoint(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">typeof</span>(ImContract), binding, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">string</span>.Format(<span style="background-color: #e4e4e4; font-family: Courier New; color: #666666; font-size: 11px">"http://localhost:{0}/Control"</span>, TestServerPort));  cp.Behaviors.Add(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> WebHttpBehavior());</span>

The two redlines above are the ones responsible for injecting the POCs the InspectorBehavior is reponsible for inserting the ContractFilter and the MessageInspector and the TestAuthorizer is the Authorization Manager test implementation.

We also need some code to raise an event:

<span style="background-color: transparent; font-family: Courier New; color: black; font-size: 11px"><span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">void</span> SendMessage()  {      var evnt <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> TestingEvent { sagaId <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> Guid.NewGuid() };       moqRA.Expect(x => x.GetChannel<TestingContract>(evnt.sagaId, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">true</span>)).Returns(channel1);      moqRA.Expect(x => x.GetChannel<TestingContract2>(evnt.sagaId, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">true</span>)).Returns(channel3);       eb.BeginNewSagaEvent(evnt.sagaId, evnt);      eb.CloseSaga(evnt.sagaId);   }</span>

And now we can look at the different options. The InspectorBehavior is just a helper class to wite the filter and/or inspector to the endpont. (The Authorization Manager is setup at the service level (i.e. for all endpoints))

<span style="background-color: transparent; font-family: Courier New; color: black; font-size: 11px"><span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">class</span> InspectorBehavior : IEndpointBehavior  {            <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">void</span> AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)      {      }       <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">void</span> ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)      {          <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">throw</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> NotImplementedException();      }       <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">void</span> ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)      {       var inspector <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> TestInspector();       endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);       endpointDispatcher.ContractFilter <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">new</span> TestFilter(endpointDispatcher.ContractFilter);                }       <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">void</span> Validate(ServiceEndpoint endpoint)      {      } </span>

The first thing I tried was the "ContractFilter". It is actually very simple to use. You inherit from MessageFilter and there are two "Match" method you need to override. One that accepts a buffer and one that accepts a (WCF) Message. WCF calls the Match method which accepts a Message.

WCF's Message class is interesting in the sense that it has a one-time touch feature. i.e. only one piece of code can read/copy it and the next piece of code which will try to do the same will fail

So the match method you can do something like the following:

<span style="background-color: transparent; font-family: Courier New; color: black; font-size: 11px"><span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">override</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">bool</span> Match(Message message)  {      var buffer <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> message.CreateBufferedCopy(Int32.MaxValue);      message <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> buffer.CreateMessage();      var r <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> buffer.CreateMessage().GetReaderAtBodyContents();      .      .      .  }</span>

Which basically means get a buffer of the message, create one copy to preserve and the get another copy for internal use and work with that to parse and verify the actual message. Unfortunetly, this doesn't really work - the message parameter is not passed as ref so the original message is lost on the first line of the method and that's it. Note that you can access the header part of the message without problem, however that's not a good fit for what I am trying to do.

The next thing I looked at the MessageInspector. Again implementing it is rather simple, you just need to implement the IDispatchMessageInspector interface. This interface has two methods BeforeSendReply and AfterReceiveRequest. We'll look at the AfterReceiveRequest method. Again we try the message copy trick:

<span style="background-color: transparent; font-family: Courier New; color: black; font-size: 11px"><span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">object</span> AfterReceiveRequest(<span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">ref</span> Message request, IClientChannel channel, InstanceContext instanceContext)  {      var buffer <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> request.CreateBufferedCopy(Int32.MaxValue);      request <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> buffer.CreateMessage();      var temp <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> buffer.CreateMessage().GetReaderAtBodyContents();      .      .      .  }</span>

This time it works since we get the request parameter as ref. At first it seemed to me that while you can inspect and alter the message as your heart wishes there is no way to say that the message is bad. One option is to alter the message to a faulty message and let the application handle it - but that means too much coupling between infrastructure and application. Another, better, option is to throw an exception.

So using the MessageInspector is a usable option. It is very good if you want to alter the incoming message but throwing an exception when the message is bad is not very clean

Which brings us to our third option Authorization Manager which, surprisingly  turned out to be the best option

<span style="background-color: transparent; font-family: Courier New; color: black; font-size: 11px"><span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">class</span> TestAuthorizer :ServiceAuthorizationManager  {      <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">public</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">override</span> <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">bool</span> CheckAccess(OperationContext operationContext, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">ref</span> Message message)      {          var autorized= <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">base</span>.CheckAccess(operationContext, <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">ref</span> message);          var buffer <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> message.CreateBufferedCopy(Int32.MaxValue);          message <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> buffer.CreateMessage();          var testMessage <span style="background-color: transparent; font-family: Courier New; color: red; font-size: 11px">=</span> buffer.CreateMessage();          .          .          .          <span style="background-color: transparent; font-family: Courier New; color: blue; font-size: 11px">return</span> autorized;      }        }</span>

Like the message inspector it receives the message as ref and like the filter it allows a single yes/no answer to decide if a message should continue or be discarded. Additionally it notifies the client that the message was rejected if that is what you choose to do (in the WebHttpBinding I used that means a 400 bad request return code)

Ok, so we've seen some of the options for implementing the Service Firewall and briefly went over thier different behaviors. The next part in this series will take a look at some of the actual implementation I did

 

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video