Categories
josephscott

HTTP Basic Authentication, A Tale of AtomPub, WordPress, PHP, Apache, CGI and SSL/TLS

I’ve been really enjoying working with Tim Bray, Pete Lacey, Elias Torres and Sam Ruby on improving AtomPub in WordPress. This work is in WordPress 2.3, which will be released later this month. You can try it out right now by downloading the beta. Sam has also started some documentation on AtomPub in WordPress at http://codex.wordpress.org/AtomPub.

There is a lot of ground to cover in the post so to start with I want to distinguish between two topics that are closely related, but for our purposes today are also separate and distinct from each other. The first is authentication, specifically HTTP Basic Authentication. The second is security, which will focus on SSL/TLS (i.e. using https:// URLs).

To start with, the AtomPub spec has a section on Securing the Atom Publishing Protocol that deals with authentication. In general, you can use nothing or what ever you want, but HTTP Basic Authentication with TLS needs to be able to work. Think of it as HTTP Basic Authentication being the lowest common denominator that AtomPub clients and servers have to support, along with TLS if you’d like.

In WordPress there are actually two ways that a user could be authenticated when using AtomPub, HTTP basic and cookies. The cookie mechanism just looks to see if you sent along an authenticated WordPress cookie with your request. Since we’d been using Tim’s Atom Protocol Exerciser (APE) for testing, all authentication was being done via HTTP basic. Which worked fine, most of the time.

I started running APE against WordPress running under different situations and I ran into a problem with authentication when PHP was being run as a CGI under Apache. When running as a server module (mod_php) PHP takes care of decoding HTTP basic for you (see HTTP basic authentication in PHP). When a using HTTP basic PHP will automatically populate $_SERVER[‘PHP_AUTH_USER’] and $_SERVER[‘PHP_AUTH_PW’] variables with the username and password that were provided. IF and ONLY IF PHP is being run as a server module (like mod_php). If you are running PHP as a CGI then those two variables won’t get created at all, ever, even when using HTTP basic authentication. And since you can’t do anything in WordPress via AtomPub without authenticating you are dead in the water. Well, not exactly.

PHP not supporting HTTP basic auth when being run as a CGI is a known issue, so folks have come up with clever work ways to work around this. One common work around is to use mod_rewrite to add HTTP basic auth into $_SERVER[‘HTTP_AUTHORIZATION’]:

RewriteEngine on
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

The idea here is that mod_rewrite watches for an HTTP basic auth attempt and then injects the HTTP header in to the PHP environment as HTTP_AUTHORIZATION. From there is it an easy job of parsing and decoding the HTTP header and manually populating $_SERVER[‘PHP_AUTH_USER’] and $_SERVER[‘PHP_AUTH_PW’] yourself. This is currently being done in the WordPress AtomPub code, so if you are on a host that runs PHP as a CGI and you have access to .htaccess and mod_rewrite then you can try it out.

Unfortunately I’ve seen times where this doesn’t work either. A modified version of this that I’ve had better success with is to pass the authentication back in via GET. Here’s an example from a test WordPress blog that redirects AtomPub authentication:

RewriteEngine on
RewriteBase /test/atompub/
RewriteCond %{HTTP:Authorization}  !^$
RewriteRule wp-app.php wp-app.php?HTTP_AUTHORIZATION=%{HTTP:Authorization} [QSA,L]

Instead of parsing and decoding from $_SERVER[‘HTTP_AUTHORIZATION’] you would do it from $_GET[‘HTTP_AUTHORIZATION’]. This isn’t exactly ideal either, but I’ve had better luck getting it to work in PHP as a CGI environments. Code to support this isn’t in WordPress AtomPub yet, but we might add it.

The four of us went back and forth on this a bit then Tim Bray asked the elephant in the room question: why doesn’t PHP support HTTP basic when running as a CGI? I didn’t have a good answer for him, so I went hunting on Google. It turns out that this has nothing to do with PHP, it is how Apache works. Apache does not pass the HTTP basic headers to CGI applications, so they never see them. This has been mentioned in several places, for brevity I’ll only quote one, from Jon Udell talking about CGI and mod_perl:

HTTP Authentication

“Note that such a module has complete access to the HTTP headers sent by the client. If you write a CGI script to enforce a security policy, à la the ByteCal example above, that script will normally see only the user’s name (HTTP_REMOTE_USER) and not the full credentials (HTTP_AUTHORIZATION).

That’s because Apache, as a security measure, withholds the Authorization header from CGI scripts. (If you really want to build a CGI-based access-control script, you can tweak Apache to make it send this header.) But an Apache/Perl authentication module, running inside the server, knows everything that Apache knows about a request.”

So far I’ve used WordPress and AtomPub as an example, but this problem is not specific to either. This is an issue with CGI applications being able to use HTTP basic authentication, and the ways people have worked around it. While there are ways to deal with this (like the two I mentioned above), they aren’t ideal and only work if you can use .htaccess and mod_rewrite.

There have been lots of alternatives to authentication that get around this issue. Lots of people have looked at this, hopefully we’ll have a generalized way of dealing with this at some point. Until then it looks like we’ll see API specific variations of authentication.

Ok, I also mentioned that we’d talk about security. This one is more to the point, if you aren’t using SSL/TLS then your communications aren’t secure. Although HTTP basic doesn’t send your plain text password and username, it is the next best thing (base64 encoded). So anyone with access to your traffic (wireless network sniffing anyone?) can easily grab your username and password. So how do you secure this authentication process? By doing it over SSL/TLS. If your web traffic isn’t using SSL/TLS it isn’t secure.

In the context of WordPress there is a trade off here. We can’t guarantee that every WordPress install is going to support SSL/TLS, so we can’t make it a requirement. That said, there is nothing in WordPress (or the APIs: AtomPub and XML-RPC) that prevent you being able to use SSL/TLS. This leaves it up the person running the WordPress blog to decide what level of security is needed.

On WordPress.com we support TLS/SSL. You can point your XML-RPC client at https://<your_blog_here>.wordpress.com/xmlrpc.php and it will encrypt the data back and forth between your computer and WordPress.com servers. Same for AtomPub, only the URL would look like https://<your_blog_here>.wordpress.com/wp-app.php.

Hopefully everyone takes away two things from this. One, you can’t depend on HTTP basic authentication working. Two, if you aren’t using SSL/TLS then your traffic isn’t secure.