Reinventing the Wheel
We’ve needed
to batten down the hatches on the application we’ve been working on to make
sure that it will be secure come go live (which is today incidentally, I’ve
been sitting on this post for a while) and the task fell to me to delve into
the murky waters of Web Service Security (WSS in future for the TLA buffs). Our
application is, in a nutshell, a smart client forms app connecting to an
ASP.NET Web Service – both in C#, and we needed to secure the users calling the
web services, as well as prevent the ‘replayability’ of the soap requests. Now,
our initial login method call is secure as we can get it, of that we’re pretty confident,
so all we needed to do was secure the rest of it. Ok... Enough back story, but
two things first:
- An important point to make at this
juncture, is that a lot of what lies below is my hackery of concepts that form
the building blocks of the Web Service Extensions framework (WSE 1, 2, and 3).
The correct way to implement WSS
would be to use it, but unfortunately at this late stage of our project it
introduced an unacceptable risk for us to run with it. (This is the excuse
known as “my boss wouldn’t let me”... It’s my excuse and I’m sticking with it
so nyah.
- This is really the kind of stuff that
should be included from the ground up in your design. We were just very lucky
that we got away with doing it now, piggy backing of our request provider code.
Todo:
·
Ensure that
multiple (replayed) calls to the service fail
·
That the
user is authenticated against both the message sender as well as a
unique identifier kept in session on the server.
·
That the unique
identifier kept in session expires properly, and also gets renewed properly
·
The failing
of authentication fails gracefully on the user interface, but is also handled
locally so as to not happen mid query (pre-emptive).
Implementation:
·
Client
contacts the server using a secure login procedure, and as a part of this
procedure the server generates a Token comprised of a unique identifier
that it saves in a dictionary collection (which will contain some kind of
custom struct to hold all of the information) along with the creation time (to
facilitate session expiry) and then returns this Token to the client for
subsequent requests.
·
On
subsequent requests the client will generate it's own unique identifier in the
form of a Key with which to sign the request as unique (possibly just
use the MessageID). The Token will then be salt-ed with this Key
and then encrypted using sha1. The token will not be sent to the server, only
the final message Signature will be sent along with the originating user
and the Key.
·
On the
server side, this message will be received and the originating user validated
against (using its own unique identifier) the database. Then the Token will be retrieved from
the dictionary, and the same process of salt-ing and sha1-ing the Token
and Key will be performed and then matched against the message Signature
as well as against it's expiry date to
see whether or not to allow the request. If this does not match, then the
request will be discarded with a thrown exception, otherwise the service will
then proceed to check the Key against a stored collection of recently
used Keys to make sure that it isn't a replayed request. If the Key
is found, then the message is rejected with an exception, otherwise the Key
is stored for future comparison, the Token expiry is refreshed and
processing continues.
That's what we've done at a very high level, and it works. We've had some trouble with synchronising between the client and server, but we'll iron that out over time. For the moment what we do is throw a custom security exception and handle it elegantly on the client by prompting the user to log in again. This opens up a new kettle of fish though, because the ASP.NET web services do not propgate the custom exceptinos very well, but that is a discussion that'll have to wait for another day.
Here are the resources I found useful, and again please note that most of them are regarding WSE, and I would highly recommend using it - I will be in future:
http://www.codeproject.com/KB/webservices/WS-Security.aspx
http://en.wikipedia.org/wiki/WS-Security
http://msdn.microsoft.com/en-us/library/ms977317.aspx