Bringing FASTer authentication to Prosody and XMPP
As our work continues on modernizing XMPP authentication, we have some more new milestones to share with you. Until now our work has mostly been focused on internal Prosody improvements, such as the new roles and permissions framework. Now we are starting to extend our work to the actual client-to-server protocol in XMPP.
Prosody and Snikket are both regularly used from mobile devices, which have intermittent connectivity. Even if it’s only a change between networks, or when driving through a tunnel for a few minutes, these things can temporarily break your connection - requiring a new one to be established.
We’ve had solutions and optimizations in the XMPP protocol for this situation for years (really… the first version of XEP-0198 was published in 2004!). XEP-0198 allows a client to reconnect to the server as soon as the network comes back, easily discover if anything failed to be sent/received due to the network interruption, and then resync any lost packets in either direction.
This effectively allows resuming and repairing the session as if no disconnect occurred, while skipping a bunch of traffic that would usually be exchanged when establishing a new session (instead, everything is simply cached from the old session).
However, there is one important thing we don’t allow the client to skip. To keep this resumption step secure, we require authentication. It’s a new connection, and we need to prove it’s from who it claims to be from.
Authentication in XMPP today
The most common authentication method for XMPP connections today is SCRAM. This is a neat password-based authentication mechanism that has many nice properties, such as allowing both the client and the server to store only a hash of the password. It also allows the client to determine that the server really knows the user’s password, and supports channel binding. These features allow the client to detect various kinds of attack.
Even though we have been using SCRAM in XMPP for many years now, it still offers more protective features today than the vast majority of online services you use - which generally all send your password to the server in plain text, albeit within TLS or HTTPS.
A new SCRAM alternative is currently being developed, known as OPAQUE, which adds even more nice properties. But that’s for future blog post… :)
However, there are some drawbacks of SCRAM (and similar mechanisms, including OPAQUE) that can’t realistically be solved. To adequately protect your password, it requires some back-and-forth negotiation with the server. In protocol speak, we refer to such situations as “round trips” - every time the client sends something to the server and has to wait for a response before it can proceed. On a very slow network, round trips can add a bunch of latency, and as anyone who has used audio/video calls or gaming online knows, latency can be frustrating and hard to eliminate from a connection.
Simpler authentication methods just have the client say “here are my credentials”, and the server say “your credentials look great, you’re authenticated!“. That’s how HTTP and most websites work today. Such approaches are quick and easy, but they don’t protect your credentials as well as SCRAM does.
Passwords are the problem
SCRAM’s protections are important for passwords. Passwords are (unfortunately) often chosen by users to be the same or similar across multiple services, and even if they are strong and unique they can be vulnerable to phishing. If leaked, many memorable passwords contain private information about the user.
We don’t want to drop any of our important password security features just to improve connection speed. So instead we found a better solution: drop passwords!
Our new solution allows the client to log in initially using a password (or any other method the XMPP server supports). After that, it can upgrade to a strong unique authentication token provided by the server, which it can use to quickly re-authenticate on future connections.
Tokens are the answer
Tokens have many advantages compared to passwords:
- They are unique to the service that generated them, so cross-service attacks like credential stuffing are useless against tokens.
- Tokens don’t need to be memorable, so they can be very long and random (both desirable properties for increasing account security!).
- As they are not memorized by the user, they can be rotated frequently without any inconvenience.
- Different tokens can be generated for each of a user’s devices, instead of sharing the user’s password across all of them. This also allows selectively removing a device’s access from the user’s account, e.g. if it gets lost or stolen.
With these security advantages, we suddenly unlock the ability to use simpler authentication mechanisms without risking the security of the user’s account or password.
Still, we can do a bit better than just sending the token to the server as plain text. Fortunately, just the kind of modern token authentication method we need has already been in development by Florian Schmaus: the SASL HT mechanism family.
HT mechanisms have the following properties:
- The actual token itself is not exchanged over the connection during authentication.
- And yet, the server receives proof that the client has the full correct token.
- The client also receives proof that the server has the full correct token (and isn’t just impersonating the real server).
- Finally, if channel binding is used, both sides receive proof that no MITM or relay attack being performed.
And… all this can be completed within a single round trip!
The protocol to achieve this has been submitted to the XSF as “Fast Authentication Streamlining Tokens”. It is in the acceptance queue, so doesn’t have a XEP number assigned yet.
Updating and integrating with SASL2
If FAST authentication was the only thing we had been working on recently, we would be happy enough. But there’s more…
In collaboration with Thilo Molitor from the Monal project, a new version of XEP-0388 (SASL 2) has been submitted. SASL 2 was originally proposed back in 2017, and it defines a new authentication protocol for XMPP (still based on SASL, so we can reuse all the existing mechanisms we already have in place).
Several features of SASL 2 are very relevant to our work. For example, it allows negotiation of session features in parallel with the authentication process. The old way required the client to authenticate, and then proceed to negotiate whatever features and parameters it wanted for the new session. With SASL2 the client can provide this information at the same time it provides its credentials. This saves yet more round trips.
As well as SASL 2, we’ve also updated a related proposal from around the same time, XEP-0386 (Bind 2). This is also a critical piece of session establishment that integrates with SASL 2.
With the work we’ve done across these three specifications - XEP-0388, XEP-0386 and FAST - we’ve essentially overhauled the entire authentication and session establishment protocol of XMPP. Even with all our additional authentication security features, it’s now possible for a client to connect, authenticate, and resume or create a session in a single request and response.
This post shouldn’t be taken as being entirely about performance improvements. It’s nice to be able to (re)connect to the server in the blink of an eye. But there are other reasons to be working on this.
As anyone who used XMPP in 2012 and 2022 knows, XMPP has been continuously evolving as both the internet and the way people use it has changed. Over time we have “bolted on” various features to the connection process to achieve this evolution.
Now, with these new changes, we are bringing all these enhancements together into a single framework that was designed for them to fit neatly into. Not only are we reducing round trips, we are also simplifying connection establishment for the next generation of XMPP developers.
When can I use all this?
Even though this is all cutting edge stuff, you’ll be able to use it much sooner than you might think!
Prosody has support for the new SASL 2, Bind 2 and FAST protocols. They are all available as community modules right now, though we intend for them to become part of the main Prosody distribution eventually.
To get started, you’ll need a Prosody trunk nightly build, and simply enable the following community modules:
To take advantage of the new features, you’ll need a compatible client. FAST is already implemented in multiple clients, and will be available from Conversations 2.11 for Android, as well as the next major versions of Monal, Siskin and Beagle for iOS and MacOS.
Gajim already has SASL 2 implemented, and other client developers have also already expressed an interest in support.
If you’re a client or library developer interested in supporting any of this, we have a test server available that you are welcome to use. Just let us know!
Do remember that all this is still very new and experimental. The relevant protocol specifications are still working their way through the XSF standards process and there may be changes to come in the future. There may also be undiscovered bugs. We encourage brave souls to help test it all in real world deployments, but if your priority is keeping a stable setup, you should probably wait a little longer before deploying any of this.
TCP Fast Open
While this post is not just about performance improvements, we’ve talked a lot about performance improvements. Therefore it’s worth noting an extra little side feature at this point.
Prosody trunk builds, when used with the new LuaSocket 3.1.0, support something known as TCP Fast Open. This is a low-level TCP extension that allows new connections to skip a round trip, by exchanging initial data packets while the connection is being established.
It’s disabled for servers by default on Linux, but you can enable it on most
modern systems by creating the file /etc/sysctl.d/tcp-fastopen.conf
with
the contents:
net.ipv4.tcp_fastopen=3
Run systemctl restart systemd-sysctl.service
to apply the changes. More
information on the sysctl configuration can be found in the Linux kernel
documentation.
In Prosody’s config, add the following in the global section:
network_settings = {
tcp_fastopen = 256;
}
Restart Prosody to apply these changes. Be aware that some networks and routers have been reported to be incompatible with TCP Fast Open (support was removed from Firefox for this reason). Although Linux has built-in recovery mechanisms that should work around such issues, if you experience trouble connecting to your server from certain networks, you may want to try turning this off again.
We’re also looking at support for TLS 1.3’s 0-RTT mode, which can be combined with FAST authentication and TCP Fast Open to achieve full connection establishment within a single round-trip. Pretty impressive!
Next steps
These protocol changes are yet another step on our XMPP authentication modernization journey. With the new protocols now written and implemented, we can start looking forward to the next milestones for the project.
In the coming months, we’ll be working on the ability to sign in to your XMPP account from third-party clients and services without sharing your password with them. Subscribe to our blog or Mastodon account and keep an eye out for that future post!
About
Prosody is a lightweight and flexible XMPP server designed with ease-of-use and extensibility in mind.