Prosody, the first password-hashing XMPP server
(At least, AFAIK!)
Recently, I have been contributing to another project outside of KDE: the Prosody XMPP server. Prosody is fairly new (the project began in 2008) and is written in Lua. I’ve been using it for a long time now, and it’s a really nice, rock-solid server. The code is also pretty hackable, being both modular and fairly clean.
My impetus for working on it was that at work we have been using the OpenFire XMPP server. It’s a big Java beast and is all but unmaintained at this point. Since I’ve had good experiences with Prosody and it already supported most of the functionality I needed, I decided to see how it handled a migration at work.
One thing it *didn’t* support was pluggable authentication. The basics for this were in existence, but the code was not yet right. After some pestering, Matthew Wild and Waqas Hussain, the two main Prosody developers, fleshed out the API necessary, and I began to work on separating out the built-in authentication code into a plugin that could be swapped out for other providers.
Once this was working, my next step was to attack something that has been bugging me on various XMPP servers to this point: plaintext password storage. To the best of my knowledge, every other XMPP server stores your credentials in plaintext or via easily reversible encryption with the key stored with your user account — in essence, simple obfuscation. The reason is because of the XMPP standards’ mandate that SASL PLAIN and DIGEST-MD5 be supported authentication methods. While SASL PLAIN transfers the password in cleartext (over the encrypted tunnel between client and server), thus lending itself well to comparison against a hash, DIGEST-MD5 does not. It consists of multiple possible authentication schemes based on the abilities of the client/server, and with different tranferred credentials depending on which scheme. It would require at least four saved tokens for every user, to be used depending on the mechanism the client and server negotiated, and updating these when passwords changed was not necessarily trivial.
So, I decided to make a tradeoff — you can store a plaintext password on the server and have DIGEST-MD5 available, or you can be limited to PLAIN and have a plaintext password go through the encrypted tunnel between client and server but have it hashed in the server’s storage. Personally, since I trust the server I’m connecting to, I’d rather have the latter. ejabberd makes a claim here that it’s more secure to have passwords sent in hashed form over the network and stored in plaintext on the server, but I think this claim is not well-reasoned. Like in many cases, security in this instance is relative. If you’re using SSL/TLS communications, which is by far the norm between XMPP clients and servers these days (in fact, in most clients you must explicitly enable plaintext authentication over unencrypted streams), the passwords are already encrypted when sent over the network — so unless you fear a MITM attack, you’re not going to be vulnerable to snooping. But you *will* be vulnerable to someone hacking onto your server and simply reading your plaintext passwords from a file, and that’s bad. I think that ejabberd’s claim is out-of-date now that encrypted communications between XMPP clients and servers are the norm, and you’ll be better off in most cases with having the one place the password is permanently stored be in a hashed form. (As you’ll soon see, you’re about to be able to have your cake and eat it too, anyways.)
The next step was deciding which hash algorithm to use. There’s something called SCRAM, which stands for Salted Challenge Response, and is an upcoming SASL authentication paradigm. It uses an open standard hash function called PBKDF2, which basically works like this: take the password, salt it with a large random series of bits, and then run it through the SHA-1 function. 4096 times. Rainbow table *that*. If you like, you can swap out which hash function you use, so you could use SHA-256 or some other function, and you can do it more than 4096 times if you like, too.
By limiting what information is passed between server and client, using secure hashes like PBKDF2, and never transmitting the password in cleartext to the server, SCRAM provides a SASL mechanism that is both highly secure and more standardized than DIGEST-MD5.
Now, back to the hashed password backend. Support for SCRAM is beginning to appear in some XMPP clients, and support was mostly coded into Prosody as well, so I asked the guy who wrote it, the excellent Tobias Markmann, to expose some bits of the SCRAM capabilities such that I could use the PBKDF2 functionality to store password hashes, as well as allowing arbitrary salts and iteration counts to be defined. He did this, and I coded up the backend, and suddenly Prosody became the first XMPP server, to my knowledge, that can securely store your password server-side.
The backend provides seamless upgrades from the plaintext backend; it will simply take the password that currently exists for the user, generate a random salt, and create the hashed credentials, removing the plaintext password.
Tobias is currently taking it a step further: he’s modifying the backend to store bits of information required by the SCRAM algorithm instead of the direct password hash. What this means is that if you end up using a client that only supports PLAIN authentication, those bits can be generated by your password on the fly and used to verify against. However, if your client supports SCRAM authentication, the backend will now be able to serve up the bits necessary for SCRAM-SASL. So you’ll have secure server storage *and* a secure exchange from client to server (in addition to the normal SSL/TLS tunnel). Tobias is, I believe, working as a SoC student on Psi — so I’m sure it’ll be working between Psi and Prosody pretty soon, if he doesn’t have it done already.
The hashed password backend isn’t currently the default, but if you’re running Prosody you can enable it by adding auth_internal_hashed to your modules and defining (in your host) the line
authentication = "internal_hashed";
Once it’s tested more thoroughly, and especially when SCRAM support is finished and working in more clients, expect it to become the default backend.
RSS
email
Print
PDF
Add to favorites
Identi.ca
Twitter
Google Buzz
Facebook
del.icio.us
Google Bookmarks
Reddit
Technorati
Slashdot
Digg
While it it nice to see improvements in this area, I’d personally always use an LDAP-directory to store account information and passwords. Not only does that mean you don’t have to reinvent how to securely store passwords, but you also get the additional benefits of being able to easily switch to a different server implementation (which is hard to do if you start storing passwords using an algorithm other servers don’t support) and being able to share accounts between different types of services. (XMPP, e-mail, …)
Well — SCRAM specifies which bits must be stored on the server. So if you want to do SCRAM authentication, you need to store those bits regardless of whether they use a file or LDAP for their data store. So this isn’t reinventing how to store them; this is making the server compliant with SCRAM and also using this for PLAIN authentication as well, to make PLAIN more secure.
Very nice – Yes ejabberd stores passwords in plain text by default but you can also plugin your own authentication module.
Now if only there were some MSN, AIM, Yahoo! and Twitter transports for Prosody..
Prosody supports plugging in your own authentication module (that was part of the work that I did), and I’m even using it at work to do extauth against a proprietary authentication system. The scope of the blog was about local password storage; necessarily when you start plugging in other authentication modules with arbitrary storage the game changes a bit
As for transports, I thought someone was working on this (at least, it was mentioned last night in the Prosody chat room) — but I’m not sure.
Transports usualy aren’t server specific. Have a look at Spectrum.im for example.
I’ve seen both — but yes, there are non-server-specific transports. Thanks for the link to Spectrum.im.
I don’t think Prosody will ever have in-process transports. It’s better to use external transports like Spectrum because if they crash, the whole server doesn’t crash, you can easily upgrade them without restarting the whole server (and you may need to, in order to keep up with the legacy protocol changes).
Spectrum also has its own developer(s?) far more dedicated to solving the issue of bridging to legacy networks, while the Prosody team can just focus on getting just the XMPP parts ‘perfect’
Sure ejabberd has pluggable authentication modules but these modules must implement a get_password/2 if you want the web admin or some command line admin features to work properly.
This is exciting news.