Category Archives: Uncategorized

Clemens and Gordon on XML Web Services

Clemens and Gordon on XML Web Services

Regarding Clemens Vasters’ article on Staying sane in an XML Web Services World:

Furthermore, Clemens assumes that XML Schema is the only way to communicate a contract, but my understanding is that WSDL does not have this limitation. I’d also like to suggest that Clemens’ rule #3 is tacitly encouraging people to use .NET DataSets, which I understand to be a first-class interop-killer. And if you don’t interop, why were you doing SOAP again? [Gordon Weakliem’s Radio Weblog]

My first comment would be that, in today’s world, XML Schema is the de facto standard for expressing types in WSDL.  While you could use another type language, I haven’t seen a lot of this in real life.  So I think the article is sound in this respect.

Second, I think Clemens’ rule #3 doesn’t necessarily encourage people to use DataSets; I think it just extends his rules to the cases where someone does use a DataSet.  However, I certainly agree – DataSets are evil in public, interoperable web services.

Finally, I don’t think I completely agree with one of Clemens’ rules:

5. WSDL is immutable. It never changes. If you change a WSDL description, it becomes a new WSDL description with its own identity (namespace-URI)

and the ASP.NET corollary:

2. If you add, remove a [WebMethod] or change any of your [WebMethod]s signatures or any of the types used directly or indirectly in any of those signatures, change the namespace!

I agree with this except for the case where you’re adding a method.  Without thinking about this too awfully much, I think adding a new method would not necessarily invalidate the contract, or change the semantics of the existing methods.  In COM, we had to be careful (i.e. we didn’t do it) about adding methods, because of the problems inherent with modifying the size of the v-table on the clients.  However, with a semi-connected web services model, I don’t see how we could get into trouble adding a method.  At least not mechanically; one could make an argument that you might have clients that never knew the interface changed, but I’m not sure that alone is enough reason to rev an interface.

Where have I been?

Sorry for no posts over the last week, with absolutely no notice.  I’ve been out of town at a new client’s office, and for some reason (despite setting everything up), I couldn’t access Radio remotely.  Time to install on my laptop, I guess.

A few tidbits, which I’ll probably write more about:

1. I never realized just how cool Managed C++ is.  Well, I’m a convert now; after spending the week integrating some new .NET code with “legacy” C++ systems, I think managed C++ is the coolest thing going.  Don’t panic, I still use C# where possible…but you can’t do everything there.

2. Wireless (802.11b) is seriously cool too!  I just installed an access point in my house, and I’m starting to wonder how I got along without it.  Now if only Chipotle would install access points, I’d be the happiest camper ever.

3. Dell’s new Latitude C640 is very cool too!  This was delivered while I was out of town; a nice upgrade from my previous 8.5 pound 300 MHz portable.  My shoulder is going to thank me, I just know it…

Chick-Fil-A Cow Stolen

A 150-pound fiberglass cow has been stolen from a Chick-Fil-A billboard in Denver.  […] The cow, one of the company’s mascots urging customers to “Eat Mor Chikin,” was apparently taken during the weekend. […] Chick-Fil-A is also offering free chicken sandwiches for a year for information leading to the safe return of the cow. [Yahoo! News]

This cracks me up – probably more than most, because this billboard is less than 4 miles from my house.  The article also says “The little bandits were very innovative” – heh…they must have played tag as children.  ;-)

Fixed Digest Authentication Sample

Oops!  Justin Rudd brought the following bugs to my attention:

1. When using Opera, the cnonce value is a base-64 encoded value which may contain the ‘=’ character. The original parsing code did not correctly handle this situation.

2. Mozilla uses the entire URI (including the query string) for the uri field in the authorization header, whereas Internet Explorer does not. The original parsing code would not correctly handle the ‘=’ characters in the header.

Both are fixed now (actually the same problem in the code).  You know, it’s funny; I had just read Brad’s post about people only testing their code on IE; I guess I fell right into that one! 

Web Services Authentication with .NET – HTTP Digest Sample

I have posted an article and sample of using HTTP Digest authentication with web services, without using the built-in IIS Digest implementation and Active Directory.  Like my previous authentication sample, this one is all managed code, and will work in shared hosting environments.

I think Digest authentication has gotten a bit of a bum rap, and it’s not in widespread use.  However, it’s a standard, well-documented protocol, and it doesn’t require clear-text credential transmission, which means you don’t have to require an encrypted channel for some classes of services.  I think it’s particularly well-suited to web services, at least until WS-Security has gained widespread acceptance.  Microsoft seems to agree, as they are using Digest authentication for their MapPoint.NET service.

Web Services Security – HTTP Digest Authentication without Active Directory

.NET HTTP Module – sample code

Introduction

Digest authentication has been around for quite some time, but has stayed in obscurity to some extent.  This is probably due to the fact that a limited number of servers support it, and a limited number of clients support it as well.  IIS also requires certain Active Directory settings to be made in order to use the built-in implementation.  However, it overcomes many of the weaknesses of Basic authentication.  In particular, it does not require an encrypted channel for communications, because passwords are not sent in clear text (as they are in Basic).  The benefits definitely outweigh the costs, as evidenced by Microsoft choosing to use Digest for their MapPoint.NET service.

In a previous article, I discussed the merits of leveraging HTTP authentication mechanisms for web services, rather than rolling your own scheme with custom SOAP headers.  Later, I posted a sample which demonstrated how to implement Basic authentication in .NET without having to authenticate against Active Directory, and without using a 3rd party ISAPI filter.  In this article, I will present an interoperable implementation of Digest authentication, also implemented in .NET managed code, without the use of the built-in IIS implementation and Active Directory.  As with the Basic sample, this code will run even on a shared server.  Note that both samples can run simultaneously on the same server, and your site can then support both Basic and Digest.

A link to download the code is at the end of this article.

If you find this post useful, please support this site and go buy yourself something on Amazon.com!

How does Digest Authentication work?

Basically, the client starts by making an un-authenticated request to the server, and the server responds with a 401 response indicating that it supports Digest authentication.  The server also sends a nonce, which can be thought of as an opaque token.  The client then re-requests the resource, sending up the username, and a cryptographic hash of the password combined with the nonce value.  The server then generates the hash itself, and if it matches the request’s hash, the request is allowed.

Let’s explore this in a bit more detail.  For all of the gory details, refer to the Digest specification.

The client first makes a request, looking like the following (some parts removed for clarity):

GET /FooService/basiconly/Service1.asmx HTTP/1.1
Accept: */*
Host: localhost:8100
Connection: Keep-Alive

The server responds with a challenge:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm=”RassocDigestSample”, nonce=”Ny8yLzIwMDIgMzoyNjoyNCBQTQ”, opaque=”0000000000000000″, stale=false, algorithm=MD5, qop=”auth”

Let’s look at the WWW-Authenticate line in more detail:

  • Digest – specifies that the server supports Digest authentication
  • realm=”RassocDigestSample” – specifies the authentication realm.  This is intended to give the client some idea of which credentials are being requested.
  • nonce=”Ny8yLzIwMDIgMzoyNjoyNCBQTQ” – specifies the nonce value the client should use for an authenticated request.
  • opaque=”0000000000000000″ – an opaque value that the server needs the client to pass back to it unchanged.  Not used in this sample implementation.
  • stale=false – indicates that the previous request was not denied because of a “stale” nonce.  If this parameter was true, it would mean that the request looked ok, and the credentials were correct, but the nonce was invalid.
  • algorithm=MD5 – specifies the hash algorithm to use when computing the digest.
  • qop=”auth” – indicates “quality of protection”.  “auth” means authentication only, and “auth-int” means authentication plus integrity protection.  This sample implementation will only send “auth”.

The client will then send an authenticated request:

GET /FooService/basiconly/Service1.asmx HTTP/1.1
Accept: */*
Host: localhost:8100
Authorization: Digest username=”test”, realm=”RassocDigestSample”, qop=”auth”, algorithm=”MD5″, uri=”/FooService/basiconly/Service1.asmx”, nonce=”Ny8yLzIwMDIgMzoyNjoyNCBQTQ”, nc=00000001, cnonce=”c51b5139556f939768f770dab8e5277a”, opaque=”0000000000000000″, response=”afa30c6445a14e2817a423ca4a143792″

And looking at the Authorization header in more detail:

  • Digest – indicates that this is a Digest authentication header.
  • username=”test” – the user name.
  • realm=”RassocDigestSample” – the authentication realm, specified in the WWW-Authenticate challenge.
  • qop=”auth” – the requested quality of protection; should match the challenge.
  • algorithm=”MD5″ – the hash algorithm used to calculate the digest.  Should match the challenge.
  • uri=”/FooService/basiconly/Service1.asmx” – specifies the requested URI on the server.  It is repeated here to ensure interoperability through proxies.
  • nonce=”Ny8yLzIwMDIgMzoyNjoyNCBQTQ” – the nonce value used for the request.
  • nc=00000001 – indicates the number of requests the client has made using this particular nonce value.  This information can be used by the server to protect against replay attacks; it is ignored in this sample.
  • cnonce=”c51b5139556f939768f770dab8e5277a” – opaque value generated by the client.
  • opaque=”0000000000000000″ – opaque value from the challenge.
  • response=”afa30c6445a14e2817a423ca4a143792″ – the 32-character digest.

The digest is calculated based on the username, the password, the realm, the nonce, the nc value, the cnonce value, the qop value, and the uri value.  For more details on the actual hash calculation, refer to the spec.

The important things to note about Digest authentication are the following:

  1. The password is never transmitted in clear-text, in contrast to Basic authentication.
  2. The server can optionally monitor and track the nc value, making it resistant to replay attacks (but at a price of having to track the necessary information).
  3. The server can carefully choose and restrict nonce values, such that a particular nonce is only valid for a certain time, only from a particular client, or only for a certain request.  This again provides resistance to replay attacks.

Sample Implementation Details

The sample code is designed to be installation-compatible with the Basic authentication example, in that the configuration, etc. are very similar; for example, they share the same users.xml file.

In this sample, nonce values are chosen to provide a minimum level of replay attack protection, but it is certainly weak.  The nonce itself is derived from the base64 encoding of the text representation of the nonce expiration time, which is 1 minute after the current server time.  So, a nonce handed out by this server will be valid for 60 seconds from the issue time.  If a client makes a request after the 60 seconds are up, a 401 response will be sent with a stale=true property in the WWW-Authenticate header.

Quality-of-protection is set to authenticate only; message integrity is not supported in this sample (although it would be very easy to add).

Installation Instructions

1. Build DigestAuthMod.dll, and copy it to your web application’s bin directory on your server.

2. Make the following changes to your web.config file (in the <system.web> section):

  • Change authentication line to: <authentication mode=”None” />. We need to disable the built-in ASP.NET authentication.
  • Add an authorization section if you wish, such as  <authorization>
    <deny users=”?” />
    </authorization>

    If you use DigestAuthMod to authenticate, you can still leverage the built-in ASP.NET authorization capabilities.

  • Add the following lines to wire the DigestAuthMod.dll into the ASP.NET pipeline.  <httpModules>
    <add name=”DigestAuthenticationModule”
    type=”Rassoc.Samples.DigestAuthenticationModule,DigestAuthMod” />
    </httpModules>

3. Make the following changes to your web.config file (in the <configuration> section), and edit appropriately:

<appSettings>
<add key=”Rassoc.Samples.DigestAuthenticationModule_Realm”
value=”RassocBasicSample” />
<add key=”Rassoc.Samples.DigestAuthenticationModule_UserFileVpath”
value=”~/users.xml” />
</appSettings>

4. Copy the sample users.xml file into your virtual directory.

The last thing you need to do is make sure all IIS authentication mechanisms (Basic, Integrated, and Digest) are turned off, and only anonymous is enabled. You can do this within the IIS Manager, or typically hosting providers will provide a way to make sure that Basic is turned off for your hosted sites/virtual directories.

Suggested Enhancements

The following is a list of suggested enhancements to this code, should you choose to use it in a production environment:

  1. Enhance the nonce-generation code.  The potential replay vulnerability can be eliminated, at the cost of processing time on the server; however, it also limits the client’s ability to pipeline multiple requests.  Weigh the cost and risk for your own application, and make the decision for yourself.
  2. Implement the message integrity checks.  There’s really no good reason not to, and it’s cheap insurance against messages getting modified in transit.
  3. Don’t store clear-text passwords; instead, store the username and the associated digest.
  4. Change the credential store to a database, LDAP directory, or whatever you need for your application.

If you have any questions, please feel free to contact me at gregr@rassoc.com.

Greg Reinacker

update (6/9/02 6:43pm MST):

The sample code has been modified to fix the following bugs:

1. When using Opera, the cnonce value is a base-64 encoded value which may contain the ‘=’ character.  The original parsing code did not correctly handle this situation.

2. Mozilla uses the entire URI (including the query string) for the uri field in the authorization header, whereas Internet Explorer does not.  The original parsing code would not correctly handle the ‘=’ characters in the header.

Many thanks to Justin Rudd for bringing these problems to my attention!

update (8/29/02 8:55am MST):

If you’re using this code on a Windows XP machine that is a member of a domain, IIS by default has Digest authentication turned on, and that will conflict with the implementation here.  Gordon Weakliem published a fix for this problem, partially reprinted here for completeness:

After some experimentation (and Greg’s suggestion), I determined that you can use the IIS ADSI interface to shut off Digest. Here’s a little bit of VBScript that does the trick on the root of the site:

' Select the virtual directory you want to modify, you'd need to determine the 
' site number if you're running more than 1 site, but that's not supported on XP Pro. 
' append a virtual directory name here to specify only a subdir 

RootNodePath = "IIS://LocalHost/w3svc/1/Root" 
Set oRootNode = GetObject(RootNodePath) 

If Err <> 0 Then 
  Display "Couldn't find the path " & RootNodePath & "!" 
  WScript.Quit (1) 
End If 

oRootNode.AuthFlags = 1 

' turn off all authentication except Anonymous 
oRootNode.SetInfo

The AuthFlags argument is a bitmask containing the authentication options for the given object, where 1 = Anonymous, 2 = Basic, 4 = NTLM and 16 = Digest. So this example sets the authentication to Anonymous. AFAICT, IIS doesn’t expose a separate property for Digest as it does for the others, so it appears that this is the only way to shut off digest.

update (3/1/03 7:10pm MST):

Updated the instructions above to fix the incorrect value in the appSettings section of the config file.

DigestAuthMod code

Sample web service

Online Travel

Online Travel

Poor customer service at Expedia.com [IUnknown.com: John Lam’s Weblog on Software Development]

Somehow, I see a lot of complaints about Expedia […]  This also makes me wonder that if the airlines get their wish and eventually customers purchase most of their tickets directly from the carrier, will they do any better?  [Gordon Weakliem’s Radio Weblog]

I travel quite a bit, and I make 95% of my reservations on United’s web site.  One advantage is web-only fares (which are only on their site); another is that if you call them, there is no middle man to worry about, like some of the horror stories I’ve read from Gordon’s references.  I’ve always gotten good service from them.  Of course, my plan only works if you primarily fly on one airline…

By the way, I notice I’ve been somewhat off-topic for a few days (still not sure what my topic is, but I know this isn’t it)…not to worry, some cool sample code and an article is ready to post, probably Monday or Tuesday.  A free mention to the first person to guess what the sample might be.  ;-)

Google for ‘Greg’

Reading this article at Business 2.0 about basic techniques for rising through the ranks at Google got me to check my various rankings, and hey, for the first time ever, I’m the number one Dave at Google.  [Scripting News]

Hmm.  I’m not even in the top 945 for ‘Greg‘…  Brad is #15 for his name, and Sam is #2.  Time to get to work on the links, I guess.  I mean, I’m sure Greg Egan is a nice guy, but I want his top spot.  :-)

You can help – link to this site!  (was that a shameless plug?  heh)

.NET Blog tool, continued

Well, that sort of defeats the whole idea behind content generation and publishing, doesn’t it? The whole power of Radio is that it’s “desktop publishing”. Your server doesn’t need to be anything except a dumb HTTP server that supports the GET protocol basically. [Drew’s Blog]

Except that my web server already has ASP.net. I get a few hundred hits a day. What do I care? :-) I understand what motivates someone like Dave to make it just require FTP and no active server components. He has a monetary interest in working with the largest crowd possible. Personally, I don’t have any such need. I’d rather prefer something powerful and dynamic over something static and flaky, but that’s just me. :-)  [The .NET Guy]

Exactly!  Let me burn cycles on my server – it has plenty to spare. :-)  As to Drew’s later point about caching and page regeneration, I actually don’t care too much about complex caching and page generation algorithms for this application.  If I have to re-generate a page every time I get a request, that’s ok with me, assuming we’re not talking about a huge amount of CPU time (which we shouldn’t be).  I’m also willing to have to tweak .aspx files and/or code to customize my site (theme, page layout, etc.), rather than have a complete customization engine…