Ian Bicking: the old part of his blog

More on single-signon

A while back I talked about Apache authentication, with the hope that people would suggest some alternatives. And they did, and for the last week or two I've been trying to figure out what to do. To recap, my goals:

Anyway, a couple things were suggested. The first I looked at was Pubcookie, out of the University of Washington. Supports Apache 1.3 and 2, and IIS. Very focused on secure single-signon across a large network. Universities typically have lots of systems administered by very different groups, and often don't have a high degree of shared trust. And every year there's a new batch of students who learn about network sniffing. But this isn't exactly my situation, but it seemed like sufficient overlap.

But I got Pubcookie installed, which was a little confusing at times but not too bad. It has an undocumented verify_fork method which calls an external process, and this makes username/password authentication easily pluggable (but not IP authentication, for instance). It uses cookies, not HTTP Basic, and a series of redirects and a CGI script for the authentication process.

But I've been hitting a number of walls, and I don't know how well it will work out. HTTPS is required for everything -- essentially programming policy into the tool. I'm not super concerned about someone sniffing an authentication cookie that is going to time out anyway, so this restriction is a little annoying. Logout is really difficult to understand or implement -- I think something is possible there, but I can't figure out quite what that is. Also, there's a fair amount of Apache configuration to change permissions around -- again, I may be able to get around that using conventions, but that's considerable added complexity (and I don't ultimately want to reconfigure Apache very often). And finally the installation is rather complicated; considering we're going to have a distinct installation for every client, and that installation includes an extra keyserver (who's purpose I don't quite understand) and a set of configuration files and several SSL keys, this makes me uncomfortable.

CoSign looked almost identical, but with nicer graphical design and poorer documentation. It also comes out of a university.

mod_auth_tkt was also suggested, but it took me a while to get around to looking at it. This looks much more promising to me -- it's fairly simple, and consists only of the identification portion. It documents the cookies you have to set for authentication (and provides a sample implementation, I think, but the documents are more than enough for me). It doesn't have multi-server single-signon out of the box, but it's not hard to figure out how that would be set up (basically a set of redirects to get the browser to visit all appropriate domains). It even has room for extra data, like user roles. It's all based on a shared secret (with signed cookies, I believe), but that's 100% okay for our situation (but probably wouldn't be for Pubcookie).

The real bummer for me is that it's for Apache 1.3 only. I see evidence of Apache 2 ports (mod_auth_tkt/2.0.0b1 in Apache identification strings), but none that are publically available. That's really frustrating. It's another one of those projects that isn't developed openly (public repository, mailing list, etc), which has been frustrating me often lately -- open source without open development is a terrible waste. Personally I don't have the C or Apache module skills to resolve this, but if we go forward with this we'd probably be willing to pay someone who does.

I also looked at mod_auth_script, which is kind of interesting (though also largely abandoned). It's got even less policy than mod_auth_tkt. I haven't tried it, but I think you just configure an authentication URL (probably internal), like /auth_script.cgi -- on every request that gets run, and it outputs special headers that indicate who, if anyone, is logged in. You could implement a signed cookie authentication, or IP-based authentication, or anything on top of this. My only desire would be the ability to add other environmental variables (e.g., REMOTE_USER_GROUPS). Essentially it would become a generic hook which didn't involve mod_*. There's an Apache 2 patch, but unfortunately I'm not sure how much I trust any of it in its unmaintained state.

I'm still shocked at how hard this is. 60% of the web servers out there use Apache. This is a really, really useful feature, and something I see people struggling with everywhere. And yet I mostly see the remnants of work to solve this, not a single robust solution or community of developers. We don't need another mod_auth_another_stupid_backend. I feel like I must be missing something?!? Sigh. Any other feedback is very welcome (and I'll report back on this yet again, no doubt).

Update: We ended up using mod_auth_tkt

Created 21 Apr '05
Modified 22 Jul '05

Comments:

We had much the same situation within a tomcat-hosted application. It had to support instantaneous detection of changing user privileges and authenticate against ldap, rdbms, and flatfile resources. I was astonished to find that there was no library already out there to do this; it seems like a pretty common need. I ended up writing a java Filter class to do the work of intercepting requests and checking whether they were authenticated; if not we send them to whatever resource is appropriate for authentication (a login form or a client cert in our case). Two small support classes solved the 'check arbitrary backend' issue.

We addressed efficiency by updating a MRU role cache in memory. Adding a bit of code to update the cache externally from events (like deleting an Employee from an ldap server) took care of the last part. It works very well and I never saw the horrible performance hit I expected from filtering every request. (This situation is why I asked you whether WebWare had filters).

Apache 2.0 has protocol Filters, which allow you to do the same thing (they're not as well documented, though). I can't see why the same approach wouldn't work well enough; you should be able to call the various mod_auth backends to do the real work, ending up with a sort of mod_meta_auth I guess.

# Jeff Duffy

The equivalent in Webware would be something in the SitePage.awake() method, or something like LoginKit which I've found flexible enough for most any authentication I've needed to do. I also have a WSGI middleware for this (that I haven't used much), and http://www.pythonweb.org has something similar for WSGI.

This is how people tend to do it... but it only works in one environment. Which is one of several reasons there's so much pressure to have a homogenous environment. But I think it should be resolvable, at least on the Apache level, without binding it to any particular language or programming environment.

# Ian Bicking

Are you trying to do authentication (checking that someone is who they say they are) or authorization (checking if user x is allowed to look at this) or both? If you're just trying to get the authentication managed in a single place, authen.py looks most of the way there to me (although it is hugely open to replay attacks unless you're changing the secret every hour or so; I'd add an expiry time and IP to the cookie and sign them with the secret too). If you're doing authorization, the implementation in authen.py doesn't seem the greatest, since you probably want more complicated rules. So where do you want to write the rules? Does each page/application manage its own authorization (assuming it can get an authenticated username), or do you want to store the authorization rules in apache's config file ("require valid-user"), or somewhere else entirely (like a database)?

When I needed SSO for Apache2, I did pretty much the same thing as your authen.py, except I made it as an authen handler instead of a header parser and used req.requires() to let me be a bit more Apache-like in the config file (I also had an analogue of groups in my login system):

PythonAuthenHandler mysso
AuthType foobar # I recall having an AuthType (even a made up one) was necessary for anything to happen
Require valid-user
or Require user bob
or Require group payingCustomers

So, the script handled both authentication and authorization, although the actual authorization rules were contained in Apache's config/.htaccess files. All the handler had to do was check that the cookie contained valid info (expiry time hadn't passed and wasn't too far in the future, user's ip was the same as the cookie-contained ip, and hash(username+group+expiration+ip+secret) matched the signature) and only shoved the username into req.user if it was valid. If req.requires() returned anything, it would also do authorization and check that whatever req.requires returned was true (groups matched or the username matched or in the case of valid_user, the username had been set).

As to handling a lack of valid authorization, I just used a straight-up 307 Redirect, ignoring the 401 code -- if you aren't using http-based authentication, there's no point in sending 401. When a user wasn't logged in, it would redirect (returning apache.HTTP_TEMPORARY_REDIRECT) to the login page like https://SecureLoginSite.InSameDomain/login?from=protectedPagesURL. Obviously, the login form would redirect back to protectedPagesURL after the user logged in and got the (domain-wide) login cookie. If everything was okay, I set req.user to username (which seemed to be all I ever needed; I'm not sure what advantage writing a fake Authorization header gets you, unless it's part of some further authorization step) and returned apache.OK, which let the page load normally.

The only problems with it were that it didn't gracefully handle POSTs to its protected area (ideally it would encode the POSTed data into the login URL and send a 303 See Other to redirect to the login URL, and when the login URL redirected back to it, rewrite the request into a POST with the encoded data) and presumably it didn't perform overly well, but that was never too important to me (it didn't have a great deal of load on it; heck, I was getting away with opening a file and reading the secret key from it on every request!) I also didn't like the way that everything was in trusted cookies which could be stolen, but if you restrict the expiry time to something resonable and track the IP it is at least somewhat safe.

So, not general-purpose or easily dropped-in (since it required a custom login app which pulls up the groups the user belongs to, checks the password is right, creates a cookie, redirects, etc.), but it worked for me. Ideally, of course, you'd use Kerberos as an HTTP authentication method, but good luck finding browser support for that, and I suppose the user would have to remember to login as username@yourwebsite.com which might be a bit unintuitive.

# anonymous

Hmm... well, authentication and authorization are always hard for me to keep track of. Lets call it identification and authorization.

The system has to support several possible techniques. For instance, some portions of the website are served up as static files directly, so Apache authorization needs to be used (require valid-user or something), or some Apache module that gives a richer set of controls. But web applications often have shifting and eclectic permissions, so I don't want to use Apache to control those permissions (or at least not require that).

One reason I don't like 307 is because (a) programs that already expect REMOTE_USER usually respond with 401 (and I want to provide REMOTE_USER), and (b) the details of the redirect are best understood by Apache, not the individual applications. But if I have to use a redirect, I'll probably put the redirect location in an environmental variable, to at least keep the configuration centralized.

The POST thing is clearly a problem. It's one of many usability issues that POST proponents ignore. Obviously a POST-understanding redirector would be nice.

One of the advantages of a signed cookie is that identification happens once, and need not be efficient, and only the cookie verification happens on subsequent requests (which ought to be quite fast). But I'd like this all in C (which is what tkt offers) instead of mod_python, in part because mod_python is challenging to maintain in its own right, and not Apache 1.3 compatible.

# Ian Bicking

Have you looked at the Yale University CAS? It may or may not have the features you need.

# Peter Zingg

I am also interested in mod_auth_tkt.

I didn't already try it but mod_auth_tkt seems to be THE solution I am looking for performant, secure and cheap authentication. With this solution, the authentication phase (password control) is independent of the access phase (ticket control). So you can use a normal php page to check the password, you can use your own password encryption for transmission on the net (better than basic authentication!!!) and you can put the passwords and users in a mySql database (better than .htpasswd files). No need for expensive SSL and certificate. You must just create the ticket cookie in your "password check" php page.

BUT there are 2 problems : - no version for Apache 2 - doesn't work if "servername" is used in the config file. Thus I suppose it cannot be used for hosting (shared web server).

If you have solutions for these 2 problems, I am interested.

# Regloor

I am also interested in mod_auth_tkt.

I didn't already try it but mod_auth_tkt seems to be THE solution I am looking for performant, secure and cheap authentication. With this solution, the authentication phase (password control) is independent of the access phase (ticket control). So you can use a normal php page to check the password, you can use your own password encryption for transmission on the net (better than basic authentication!!!) and you can put the passwords and users in a mySql database (better than .htpasswd files). No need for expensive SSL and certificate. You must just create the ticket cookie in your "password check" php page.

BUT there are 2 problems : it doesn't work if "servername" is used in the config file. Thus I suppose it cannot be used for hosting (shared web server). In addition, there is not yet a version for Apache 2.

If you have solutions for these 2 problems, I am interested.

# Regloor

I am also interested in mod_auth_tkt.

I didn't already try it but mod_auth_tkt seems to be THE solution I am looking for performant, secure and cheap authentication. With this solution, the authentication phase (password control) is independent of the access phase (ticket control). So you can use a normal php page to check the password, you can use your own password encryption for transmission on the net (better than basic authentication!!!) and you can put the passwords and users in a mySql database (better than .htpasswd files). No need for expensive SSL and certificate. You must just create the ticket cookie in your "password check" php page.

BUT there are 2 problems : it doesn't work if "servername" is used in the config file. Thus I suppose it cannot be used for hosting (shared web server). In addition, there is not yet a version for Apache 2.

If you have solutions for these 2 problems, I am interested.

# Regloor

Is there any documentation online for mod_auth_tkt. If so, could you please tell me where i can find .

Thanks
# Prabu

Can this module authenticate against active directory LDAP server. Please advise.

Thanks Prabu

# Prabu

Perhaps you ought to look more closely at cosign. I might say that it has less documentation than pubcookie because there's much less to document. Certainly it addresses some of the issues you're complaining about: it doesn't require HTTPS, logout is simple, installing the filter is quite easy. Good luck!

# Wes Craig

Interesting site, it is well written. Registration not so was pleasant, and with scripts of a problem. Let's vary references, or banners. Write, how solve

# John

Good site. The answer

# Liza