Monday, May 22, 2006
« Resharper 2.0 Released Today | Main | Introduction to C# Presentation »

I'm developing a web service in ASP.NET 1.1 through which I need to transfer files.  There are a myriad ways in which I could accomplish this, but rather than roll my own, which I am sometimes inclined to do, I wanted to use some built-in functionality.  This functionality can be found in WSE 2.0 and DIME (Direct Internet Message Encapsulation) - thanks Fabio for reminding me.  Believe me, it pains me that I cannot use WSE 3.0 and MTOM with .NET 2.0, but such is not possible with this project - at least not yet.  But transferring the files would be orders of magnitude easier via MTOM, but alas, here we are.

Shortly after I set up the web service and tested it locally, I wanted to ensure that it would work remotely.  The machine to which I have it deployed is running on a Virtual PC running Windows Server 2003.  I could see the nice ASP.NET web service description page navigating via my browser to the .ASMX file, but when I attempted to access it via code, I was first presented with the following error message:

System.Net.WebException: The request failed with HTTP status 401: Unauthorized.
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
...

Oh, that's right, I need to include some credentials to access this particular web service.  Therefore, I added the following snippet of code below my client proxy declaration:

ServiceWse svc = new ServiceWse();
svc.Credentials = CredentialCache.DefaultCredentials;

Once I did that and ran my test harness I was greeted with a slightly more inocuous error:

Microsoft.Web.Services2.Security.SecurityFault: An error was discovered processing the <Security> header ---> System.Exception: Creation time in the timestamp can not be in the future.
--- End of inner exception stack trace ---
at Microsoft.Web.Services2.Security.Utility.Timestamp.CheckValid()
at Microsoft.Web.Services2.Security.Utility.Timestamp.LoadXml(XmlElement element)
at Microsoft.Web.Services2.Security.Utility.Timestamp..ctor(XmlElement element)
at Microsoft.Web.Services2.Security.Security.LoadXml(XmlElement element)
at Microsoft.Web.Services2.Security.SecurityInputFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services2.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services2.InputStream.GetRawContent()
at Microsoft.Web.Services2.InputStream.get_Length()
at System.Xml.XmlScanner..ctor(TextReader reader, XmlNameTable ntable)
at System.Xml.XmlTextReader..ctor(String url, TextReader input, XmlNameTable nt)
at System.Xml.XmlTextReader..ctor(TextReader input)
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
...

As it turns out, WSE will utilize a timestamp to verify security tokens between the SOAP sender and receiver and if the two computers' clocks vary by more that the default tolerance of five minutes, it will fail.

One solution would be to synchronize the client to the server, but that's a pain and may not properly work in a global scale which would be required by my particular application.

This baffled me for a few minutes because the time on my local PC and the time on my VPC were the same; that is, they appeared the same.  I quickly saw that the VPC's time, though the same with respect to hrs, minutes, and seconds is relative to Pacific Time whereas my laptop is on Mountain Time.  So they ultimately varied by 60 minutes, well over the default tolerance.

The solution is to alter the web.config file on the server and the client application's configuration file to include an override to the tolerance.  I set it to allow for up to a day (24 hrs) in difference to accommodate for any discrepancy that may arise.

<configuration>
   <configSections>
      <section name="microsoft.web.services2" type="Microsoft.Web.Services2.Configuration.WebServicesConfiguration, Microsoft.Web.Services2, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   </configSections>
   <microsoft.web.services2>
      <security>
         <timeToleranceInSeconds>86400</timeToleranceInSeconds>
      </security>
   </microsoft.web.services2>
</configuration>

I'm actually very happy that I had this situation arise now rather than after I had deployed because I now remember that I have to test for time zone differences.

Friday, July 28, 2006 7:30:00 PM (Mountain Standard Time, UTC-07:00)
Great!

I've beeing looking for this for a long. Thank you!
Thursday, January 25, 2007 5:57:00 AM (Mountain Standard Time, UTC-07:00)
Thanks for posting this discovery. I did some exploring of my own & thought I'd clarify one of your points.



The time zone of the two machines is irrelevant. All timestamps are converted to UTC before they'er serialized. Your original problem was that the UTC time of the two machines was off by an hour. It was masked because the time zones were off in the other direction by an hour.



In practical terms two "real-world" computers' UTC times are rarely off by more than a few minutes. If they are, people tend to show up at meetings & the like at the wrong times, cron jobs tend to interfere with OLTP activity, that sort of thing. In any case, it's probably appropriate for the server to reject a SOAP request with a timestamp that far off. The motivation behind using timestamps is to prevent spoofing attacks & the like, and allowing a 24-hour window is probably a little extreme, IMHO. So I'm setting this property (again, thanks for the tip) to allow an hour's leeway.



Just my .02.



Jon
Wednesday, May 21, 2008 1:25:00 AM (Mountain Standard Time, UTC-07:00)
This kind of behavior is designed to be a defense against replay attacks. 5 minutes is a poor compromise - if you are really worried about replay attacks, it should be lower. If you are not so worried, 5 minutes is a bit of a pinch, enough to cause problems on a production service.



It's nice to know how to customize it.
quillbreaker
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, i, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview