Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent duplicate HTTP requests with Windows Authentication

I'm working on an WCF-based client/server application (WCF is self-hosted, not in IIS).

The WCF service has an operation to upload a chunk of data to the server. The contract roughly looks like this:

void UploadChunk(int clientId, byte[] chunk);

We are using Windows Authentication (Kerberos/NTLM) so we cannot use streaming here.

The binding looks like this (client- and server-side):

new BasicHttpBinding
{   
   Security = new BasicHttpSecurity
   {
      Mode = BasicHttpSecurityMode.TransportCredentialOnly,
      Transport = { ClientCredentialType = HttpClientCredentialType.Windows },
   },
   MaxReceivedMessageSize = 0x7fffffff,
   ReaderQuotas = { MaxArrayLength = 0x800000 },
};

The client talks to the service via proxy objects derived from System.ServiceModel.ClientBase<TChannel>.

All of this works perfectly fine, but we observed that the WCF client sends each HTTP request twice, once without auth header and once again with the correct auth header. This is problematic because the requests will be pretty big and this behavior causes the request size to be two times the actual chunk size.

I already found out (https://weblog.west-wind.com/posts/2010/Feb/18/NET-WebRequestPreAuthenticate-not-quite-what-it-sounds-like) that setting WebRequest.PreAuthenticate to true remembers the auth header and reuses it for subsequent requests.

However from what I've seen up to now WCF does not expose a mechanism to modify the WebRequest instance.

Is there any solution for this problem`?

like image 775
Florian Greinacher Avatar asked Mar 23 '16 17:03

Florian Greinacher


1 Answers

For Windows Authentication there will always be a challenge response (401) for your first request .

If you're in control of all clients I think the most practical solution is to implement an operation with a minimal payload.

Operation void IsAuthenticated() should do. For each client proxy instance you would then call IsAuthenticated before UploadChunk.

The IsAuthenticated request would get you over the 401 challenge response without sending the large payload but will authenticate the connection. Subsequent requests for that connection will not be challenged.

Edit :

The behaviour I described seems to only be applicable with IIS 8. So I took a closer look with two http.sys traces, one for an IIS hosted service and one for a self hosted service.

The IIS hosted service seems to utilize some sort of optimization with regards to authentication. The first request for the connection is authenticated using the Authenticator Sspi Authenticator. Subsequent requests are authenticated using the Fast Authenticator.

None of these events are present in the self host trace which leads me to the conclusion that self hosting is not optimized for Windows Authentication.

http.sys - trace IIS

http.sys - trace self host

Then I found this blog entry that proposes a solution using NTLM, a custom binding and the unsafeConnectionNtlmAuthentication setting for the HTTP transport. If you're willing to only use NTLM and the security concerns highlighted in the documentation are not a concern this seems to provide the behaviour you're looking for as per the http.sys trace.

http.sys trace - self host with custom binding

For the server use binding

<customBinding>
    <binding name="myBinding">
      <textMessageEncoding messageVersion="Soap11" />
      <httpTransport authenticationScheme="Ntlm" unsafeConnectionNtlmAuthentication="true"/>
    </binding>
  </customBinding>

For your client you can use a regular basicHttpBinding with Ntlm security:

<basicHttpBinding>
    <binding name="BasicHttpBinding_ITest">
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Ntlm" />
      </security>
    </binding>
  </basicHttpBinding>
like image 71
Amith Sewnarain Avatar answered Oct 23 '22 16:10

Amith Sewnarain