[OpenID] Nonces generated by the server?
andrewarnott at gmail.com
Wed Apr 1 01:59:18 UTC 2009
I didn't realize you were the author of the Perl library. I probably would
have been more gentle with my words. :( Sorry. It's often a thankless job
as a library author (I know) and it's a very big job to get it done right
Further discussion inline below, including the nonce discussion you asked
for... Good luck with your Perl implementation of the feature!
2009/3/31 Martin Atkins <mart at degeneration.co.uk>
> Andrew Arnott wrote:
>> I'm also somewhat curious about how many OpenID consumers actually
>> do nonce checking. Net::OpenID::Consumer for Perl actually ignores
>> the nonce altogether and implements its own timestamp checking due
>> to legacy code for OpenID 1.1, and seems to be vulnerable to replay
>> for up to 30 seconds after a positive assertion.
>> The author of the Perl library ought to be ashamed. This kind of thing
>> reduces my confidence in using OpenID at any site other than one that I
>> wrote the library for myself.
>> Although this is what OSIS testing is all about. Hopefully there is a
>> test to catch RPs and OPs that don't check the nonce for replays.
> Yes. As the maintainer of that library (though not its original author), I
> am ashamed, which is what prompted the question in the first place.
> I'd love to have a test in the test suite for this.
> RPs only need to do this checking when they're running in stateful mode,
> right? Since stateless RPs have nowhere to store state they can't retain a
> history of nonces.
> Can you share some high-level details about your nonce-checking
> implementation? Specifically how you persist the previous nonces, when you
> expire them, etc?
*Relying Party side of things*
DNOI (dotnetopenid) generate nonces and add a "dnoi.request_nonce" parameter
to the return_to url if discovery on the user supplied identifier suggests
that the OP is a 1.x version of OpenID. The RP does not store nonces it
generates. In stateless mode in never stores them at all, relying on the OP
to check for unique nonces. In stateful mode, the RP only stores nonces
that are included in positive assertions it receives, whether those nonces
are dnoi.request_nonce or openid.response_nonce values. It uses a
'blacklist' style instead of a whitelist style for these reasons:
1. Causing an RP to generate an authentication request is cheap. In a
DoS attack, an RP could quickly fill its memory with nonces by just asking
the RP to start an auth request without ever following up on it. By waiting
until there's a positive assertion carrying a nonce, it becomes more
expensive to run such a DoS attack and thus mitigates the risk to the RP.
But it also means you will have a record of the claimed_id of each of the
logins involved in the DoS, so you can block that OP endpoint later if it
ever became a problem.
2. Since the RP sometimes generates the nonce (for openid 1.x) and
sometimes does not (openid 2.0), it provides a single place to store and
check the nonce in the code to only do so upon receipt of the assertion.
3. The OP generates nonces that it never will store or check for openid
2.0, so again, it made sense for the nonce generation code to not store, and
for the consumer of the nonce (whether RP or OP) to just add the nonce to a
store and check for a conflict at that time and have cleaner code.
Sure there are reasons to argue the other way, but the above is why I chose
this way of doing it.
Oh, and here's a detail to be mindful of that DNOI had a bug regarding for a
lot of versions: the nonce should be combined with the OP endpoint (in a
secure way) to make a unique nonce that you store and check for collisions
with. The reason for this is to avoid an RP falsely detecting a replay
attack when one OP endpoint happens to generate the same nonce as another OP
endpoint. An OP endpoint can legally just increment an integer (1, 2, 3) to
be its nonce and be unique for itself. If two OPs do this, you can imagine
how likely a collision would be. Nonces out there are typically more random
than that, but it's still a slight concern.
Provider side of things
On the OP side it gets much more interesting. I have a blog post on it
Basically, OpenID 1.1 Providers provide no replay protection for their
customers. That wasn't good enough for me, so with DotNetOpenAuth 3.0 (DNOA,
previously known as DNOI), I added replay protection from the Provider side
even for customers logging into 1.1 RPs. My blog post discusses how this is
The basic 2.0 scenario though is that the OP generates nonces, and only
stores the ones that come back in check_authentication messages.
The advanced 1.1 scenario is that the OP always forces private associations
for positive assertions (read: RPs are forced into dumb mode) and thus the
check_auth comes back to the OP, and the OP can check its proprietary
*Clearing of expired nonces*
Since most web sites don't have associated services that run constantly or
periodically (yes, there are cron jobs, but not on Windows as much as on
Linux, and I'm targeting Windows), I just had a simple algorithm that was
roughly this: every 20th nonce check should also cause an expired nonce
sweep to occur, where expired nonces are removed from the database.
*Web farm considerations*
It's vitally important that if your library is used by a site that is hosted
by a web farm, that the nonce store is shared by all servers (including
multiple processes on the same server, I think called a web garden) in a
transaction supporting database. Even if your web farm has a sense of
affinity so the same user tends to be directed to the same server in the
farm, a hacker will have a different IP address and therefore be directed to
any random server, and if the nonce store is not shared, he will succeed at
replaying a message and spoofing a user's identity!
> I'm wondering if it would instead be simpler to use a client-generated
> nonce in the return_to URL, as you note that DotNetOpenID is doing for 1.1
> requests, thus allowing the nonce checking to be a whitelist rather than a
> blacklist and the nonces to be in a known format that I can optimize for.
> general mailing list
> general at openid.net
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the general