Monday, March 30, 2009

How to pretty much guarantee that you might get an email address with OpenID

OpenID itself is just an authentication protocol.  It takes OpenID extensions to get more information about the user like their name or email address.  In fact there are two popular extensions that can provide this kind of information: Simple Registration (sreg) and Attribute Exchange (AX).  A web site that wants to accept OpenID logins (this site is called a relying party, or “RP”) and also gather the user’s email address at the same time may do so, but unfortunately it is quite complicated to get the best user experience.

OpenID Providers (aka “OP”) can support either or both of these extensions.  And while the sreg extension is straightforward and consistently implemented, AX is divided.  Let me explain.  If you want an email of a user and you’re using the sreg extension, just ask for the value for “email”.  Simple.  But if you’re using AX, you have to ask for these three attributes:

  1. http://axschema.org/contact/email
  2. http://schema.openid.net/contact/email
  3. http://openid.net/schema/contact/email

Why on earth?  Well, AX is extensible, so any attribute URI can be used to refer to some value that you want.  Unfortunately, before AX was a finalized spec several popular OPs picked up support for it and made up different ways of describing the simple user’s email attribute.  The very unfortunate thing is that once AX standardized on one Type URI form for the common attributes (#1 on my list above), many of these OPs didn’t bother to update their code to support the official attribute type URI.

What that means for RPs that can request authentication against arbitrary OPs is that they have to request all three of these attributes and then check for any of these three attributes to have values in the AX response.  But that’s not all, of course…

Some OPs don’t support AX at all, so you also have to send an sreg extension request to fetch the email address, and an RP then has a total of four places in the response to check for an email address.  Why not just use the unified sreg, you ask?  Because Google doesn’t support sreg – only AX. 

Oh, and Google will only give you an email address if the RP indicates that it is an AX “required” attribute.  Google completely ignores attribute requests marked as “requested”.

And Yahoo! doesn’t support either sreg or AX extensions at all.  They plan to, but as yet they don’t give out any user information to RPs. 

So if you request email addresses via sreg and AX, and for AX you ask for the email in all three forms, and if you mark them as required, you have a pretty good chance of maybe getting a user’s email address.

OpenID is really cool.  But retrieving attributes about a user is not.  AX is a great spec, but very, very poorly adopted.

Monday, March 16, 2009

Need access to that internal? Don’t touch that dial!

The blessing and curse of open source is that the source can be easily changed. 

Internal types and members don’t need to be backward compatible with previous versions. This makes fixing bugs and feature enhancements much easier in future versions of the software.  Making types and members internal cuts down on documentation that must be written, read and maintained.  It protects callers from misusing a type or member and unknowingly introducing a bug, which could be a security problem.  It makes consuming the public area easier since reading over a smaller public API is quicker than reading over an enormous one.

Coding Horror: I’ve read a suggestion that since DotNetOpenId is open source, all types and members within it might as well be made public rather than internal so that it’s easier to use.  Just because something is open source does not mean that we throw design virtues out the window.  The internal scope exists in every statically typed language because it is so useful – not because closed source software somehow needs it to hide their implementation details. 

Coding Horror: I’ve also read emails that casually mention that the writer wanted access to a method in DotNetOpenId that looked useful but was internal.  So they made it public and started using it.  This person did not understand that that method was not designed for use outside of a very specific scenario, and they introduced a significant security hole in their application by misusing it.  It turned out that there was already a public member that did exactly what they wanted.  Had they focused their efforts looking over the public API for the right way to get the feature they needed, as they would have been forced to do if it was a closed source library, they would have found it and never had the security hole.

There is only one valid scenario I’ve seen or heard of where DotNetOpenAuth had its types legitimately made public when they were designed to be internal.  It is for the upcoming OSIS interop tests, and the tests specifically need to do the wrong thing, in order to test that the remote party reacts by rejecting the request.  A good library is designed to make doing the right and secure thing easy to discover and easy to do.  Doing the insecure thing should be difficult or impossible.

In DotNetOpenAuth’s case, doing the insecure thing was impossible in many cases, so yes, types had to be elevated from internal to public in order for these interop tests to work.  But since this is a corner case and not something that any production web site should be doing, these types are only made public in a dedicated branch in source control that will never be merged back into main development, although updates from main can certainly be merged into the osis branch. 

So my plea for you, dear reader, if you see a useful-looking type or member in DotNetOpenAuth or DotNetOpenId, please ask yourself if a public member with the functionality you’re looking for already exists.  And if you don’t think there is, ask the dotnetopenid@googlegroups.com mailing list before you make the change.  Do yourself a favor: don’t touch that [access] dial.

Wednesday, March 11, 2009

DotNetOpenAuth 3.0 Beta 2 released

DotNetOpenAuth, previously named DotNetOpenId, is getting nearer to its major 3.0 release.   With beta 2, we have a security reviewed, feature complete library for .NET use of the OAuth and OpenID protocols. 

Although Beta 1 was very rough and was not recommended for use in production, Beta 2 has passed enough security, interop and stability tests to warrant live web sites to use this version.  It’s not “release” quality yet, but mostly that’s due to needed stabilization time and to hear feedback from early beta 2 adopters to apply any final interop fixes for the final version.  DotNetOpenAuth v3.0 Beta 2 has some very significant features that are new since the last release of DotNetOpenId v2.x.

Download beta 2 from Google Code or Ohloh.  (The project sites are still called DotNetOpenId, but you’re at the right place).

Major enhancements since beta 1:

  1. Much more stable
  2. Classic ASP support
  3. Tamper protection of callback arguments
  4. ASP.NET State Server and other serialized session stores support

Check out the VersionChanges wiki page for a more complete rundown of the changes since v3.0 beta 1 and earlier versions of DotNetOpenId.

Please leave feedback on the new version here as a comment or at the dotnetopenid@googlegroups.com mailing list.  Questions?  Send them to the same mailing list, or post them at StackOverflow.com and tag them with “dotnetopenid”.

Saturday, March 07, 2009

Replay protection for OpenID 1.x relying parties

If you’re writing an OpenID Provider, you should have a strong appreciation for the security of your customers’ identities that you will be protecting.  One aspect of that protection is against replay attacks, where a man-in-the-middle sniffs the identity assertion from a Provider and replays it against the same relying party and manages to log in as the victim.  OpenID 2.0 provides built-in protection against replay attacks, but that leaves OpenID 1.x users vulnerable.

The recent OpenID Providers hosted by the big Yahoo!, Google and Microsoft have mitigated against the security problems in OpenID 1.x by refusing to log their users into OpenID 1.x relying parties.  This is secure for their users, but less helpful in getting them to log into those sites.  There are still quite a few OpenID 1.x relying party web sites out there.  It would be great if we could allow them to log in and yet still offer them protection.

It turns out that replay protection for OpenID 1.x is not new.  On the relying party side, replay protection can be added with custom parameters added to the return_to parameter of the authentication request.  The Janrain OpenID and DotNetOpenId libraries do this already.  But since not all OpenID 1.x relying parties can be relied on to have implemented their own replay protection, an OpenID Provider cannot assume any particular relying party is safe unless it is an OpenID 2.0 site.

New with DotNetOpenAuth (a.k.a. DotNetOpenId v3.0) there is a way to have your cake and eat it too.  OpenID Providers that use DotNetOpenAuth as their OpenID library will provide replay protection for all their users, regardless of the OpenID version supported by any arbitrary relying party web site.  It does this by refusing to use a shared association during authentication if that authentication request comes from a 1.0 RP.  Instead, it generates its own private association and changes the assoc_handle parameter in the response.  The RP is then forced to verify the assertion by calling back to the Provider.  This is where the Provider can apply its own replay protection, which is exactly what it does.

With replay protection now extended to all versions of OpenID relying parties, it seems that the only security hole left in OpenID 1.x that would justify a Provider in refusing to work with them is RP site verification (a.k.a. RP discovery).  But to date every Provider, even the big ones previously named, work with OpenID 2.0 RPs even if they don’t happen to support RP discovery, so this isn’t even an issue.

Will we see Yahoo! and others start working with 1.x OpenID RPs?  I doubt it.  But I think they could (securely) if they wanted to.

OpenID association poisoning

As part of the OpenID protocol a relying party often establishes shared secrets (called ‘associations’) with identity providers that are used to verify identity assertions.  It occurred to me that an OpenID relying party might easily introduce a major security hole in the process of establishing an association that could allow identity spoofing.

Each association is assigned a handle, which is a name by which the relying party and the provider will refer to the shared secret in later transactions.  The potential security hole is possible because the Provider alone determines the association handle.  If the relying party is not careful in saving associations it creates, a rogue Provider could hijack another Provider’s association with the relying party and thereby gain the ability to assert the identity of any user from the other Provider.  Here’s a scenario:

  1. Victim hosts his identity with GoodOP, and has logged into a vulnerable RP and saved some private data.
  2. Hacker hosts EvilOP, which is a carefully contrived Provider rigged to hack into RPs.
  3. Hacker attempts to log into RP as any account hosted by GoodOP and can thereby discover the handle CompromisedHandle of the shared association between RP and GoodOP.
  4. Hacker instructs EvilOP to assign CompromisedHandle as the handle for the next association it creates with an RP.
  5. Hacker starts a login at RP with a Claimed Identifier that points at EvilOP.  The RP then establishes an association with EvilOP as a preliminary step to the login process. 
  6. EvilOP tells the RP of the new association and says the handle for it is CompromisedHandle
  7. RP is vulnerable and overwrites the shared secret it has with GoodOP with the new one it established with EvilOP.  Yet CompromisedHandle is still associated with GoodOP in the RP’s associations table.
  8. Denial of Service: The RP can no longer log in users from GoodOP, because the shared secret between them is wrong and the RP will reject identity assertions from GoodOP due to invalid signatures.
  9. Identity Spoofing: EvilOP now can write identity assertions on behalf of GoodOP such that RP thinks they are from GoodOP.  Hacker can use EvilOP to write assertions and log in as anyone who has an account with GoodOP.

The good news is that having come up with this possible security hole, I did a check of DotNetOpenId and Janrain’s OpenID Ruby library.  Neither one was vulnerable to this.  Since all of Janrain’s libraries are similar to each other, I ended my investigation because it was likely that all the other Janrain libraries were also secure in this regard. 

Still, this is another argument for web sites to use standard libraries for their OpenID support rather than trying to implement OpenID themselves.  There are just too many potential security holes for a webmaster to avoid them all unless authentication is truly his focus and passion.