--- title: On WebAuthn, MFA, and local SSO date: 2023-09-28 tags: [security, code] description: Multi factor authentication (MFA) is gaining steam. Time for me to take a step back and get an overview. --- Multi factor authentication (MFA) is gaining steam. The ecosystem is evolving and laws such as [NIS-2](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32022L2555) start making it a legal requirement. At the same time the ecosystem is becoming so diverse that two people working on the topic might work with completely different terminology and software. Time for me to take a step back and get an overview. I will try not to do the old "passwords are bad and MFA is better" dance. I will still do the comparison, but only skim the boring parts and quickly move on to the (hopefully) more interesting details. ## Passwords Passwords have been used for authentication for a long time, even though they have many issues: - Humans pick passwords that are easy to guess - Humans easily forget their passwords, so they keep them on sticky notes on their screen - Passwords are typed in, so they can be extracted by keyloggers - Passwords are sent over the wire, so they can be extracted if there is insufficient transport encryption - Passwords are sent to servers, so the server operators can get access to your other accounts if you reuse passwords (or type in the wrong password) - Passwords are stored on the server, so they can be extracted from database leaks if they are not sufficiently hashed and salted In short: Passwords are in too many places. ## Two Factor Authentication (2FA) Now, replacing passwords all at once didn't seem realistic. Also, all the alternatives we had available came with issues of their own. So instead of replacing passwords, we started adding other authentication mechanisms on top. ## One-Time Passwords A very common approach to two factor authentication is to send one-time passwords over a side channel such as [email](https://notes.xoxco.com/post/27999787765/is-it-time-for-password-less-login), SMS, or some proprietary smartphone app. These side channels are often insecure though. A better approach is to use [Time-Based One-Time Passwords (TOTP)](https://www.rfc-editor.org/rfc/rfc6238): A one-time password is generated based on the current time and a pre-shared symmetric secret. The secret is commonly transferred via QR codes. The main downsides here is that phishing is easy: Attackers can just call users and say "I need you to verify your identity by telling me the current code" and they are in. Also, the symmetric secrets cannot be hashed on the server. ## Something you own Many people say that the two factors in "two factor authentication" are knowledge and possession. Sometimes they also add biometrics ("something you are"). I don't buy that. Computer science is built on the idea that everything is data. All these factors will eventually collapse into bits, aka "something you know". A common implementation of possession is that websites store a long lived cookie on a device. This device is now "trusted". Logging in from an untrusted device is then blocked or at least a warning is sent to the user. This process of course needs to be bootstrapped somehow. This simple cookie-based implementation assumes that session cookies are short-lived and trust cookies are long-lived. Users who never log out do not get any benefits. And users who regularly delete all cookies get confusing warnings ("login from new device"). So I am not a fan. More involved implementations use something like a trusted platform module (TPM) where a secret is stored but can never be extracted. Knowledge of the secret is replaced by possession of the TPM. As far as I know, real-world TPMs are not perfect. I fear we might get into trouble if our crypto protocols start to depend on TPMs. Instead, we should think of them as obstacles. They make attacks harder, but don't give any guarantees. ## Multi Factor Authentication (MFA) My impression is that the term "2FA" is slowly being displaced by "MFA". The "multi" is not so much about "two or more factors", but more about a move to a more open understanding of authentication. Since we started thinking about 2FA, the ecosystem has evolved and we are slowly ready to leave passwords behind. The discussion is no longer just about amending passwords with a second factor, but about finding new and better ways to do authentication in general. This also means that we are in a time of change, kinda post-password. We already know that passwords are a thing of the past, but we have not yet settled on a new approach. ## Single Sign-On (SSO) Let's shift gears for a moment and think about SSO. With SSO we get a third party: You (via your *client*) want to log in to a website (*relying party*), but the actual authentication is done by an *identity provider*. This has two implications: - Everything is centralized to a single login, so you can concentrate on making that really secure, e.g. by picking a more complex password or some elaborate MFA scheme. - The communication between the relying party and the identity provider is completely automated, so it can use more complicated but stronger mechanisms (long random passwords, challenge-response, asymmetric cryptography, …). ## Local Identity Providers If the identity provider is running on your devce, the authentication doesn't need to involve the network, which significantly reduces the attack surface. One example of this idea are password managers. They deliver on the first aspect (a single place that can be really secure) but not on the second (a password is still sent to the relying party). Their main benefit is that they are fully backwards compatible with existing websites. Your SSH keys also act as local identity providers. Compared to password managers, they use a much better protocol to communicate with the relying party. ## WebAuthn / FIDO2 [WebAuthn](https://www.w3.org/TR/webauthn/) is a new JavaScript interface. Even though it is often talked about in the context of MFA, I believe it is more productive to understand it as a way to authenticate against a special kind of local identity providers called "authenticators". It was created by the [Fast IDentity Online (FIDO) Alliance](https://fidoalliance.org/). The commonly used term "FIDO2" is (as far as I understand) not a specific standard, but an umbrella term for "WebAuthn and related stuff". I finally got around to read the WebAuthn spec and I was a bit shocked. You want your security related specs to be clear and specific. WebAuthn is not that. Let me illustrate that with a quote: > A Client-side discoverable Public Key Credential Source, or Discoverable > Credential for short, is a public key credential source that is > ***discoverable*** and usable in authentication ceremonies where the Relying > Party does not provide any credential IDs, i.e., the Relying Party invokes > `navigator.credentials.get()` with an ***empty*** `allowCredentials` > argument. This means that the Relying Party does not necessarily need to > first identify the user. > > — I will get to discoverable credentials later, but suffice to say that I did not understand them from reading this paragraph. (It is actually even worse because they changed the wording mid-way, so all the interfaces use the term "resident keys" instead.) Looking past the overly complex jargon, there is a lot of good stuff there. I will skip over most of it because many posts have already been written about that. I will only say that it is a lot like SSH in that it uses public key cryptography to communicate between the authenticator and the relying party and that it has some other benefits (e.g. use of TPMs) that even put it ahead of SSH in terms of security. ## Client-side MFA So far we have discussed that WebAuthn is a form of local SSO. It can be used as a building block for MFA either by using it as one of several authenticaion steps on the relying party, or by requiring user verification on the authenticator. Let's look at the latter option. How user verification is implemented depends on the authenticator. Another benefit of using a local identity provider is that it has access to a wide range of sensors, so apart from passwords (or PINs), we also see facial recognition or fingerprints. The relying party can select whether user verification should be discouraged, preferred, or required. It is preferred by default. Discouraged user verification is useful to avoid redundant verification if the relying party has already asked for a password. (Redundant user verification could become a real usability issue as we add MFA in more and more places.) Required user verification is only useful if the relying party can trust the authenticator. There is a a certificate based ["attestation"](https://developers.yubico.com/WebAuthn/Concepts/Securing_WebAuthn_with_Attestation.html) system for that, but a relying part must jump through some additional hoops to use it. If you are a relying part and absolutely depend on user verification, make sure to check attestation. Apart from user verification, authenticators can also check user presence. However, relying parties have no control over that and it is always required. That means that authentication without user interaction is not possible. ## Moving beyond passwords As explained in the beginning, the goal is to get rid of passwords completely. The buzzword a year ago was "passwordless", the current iteration is "passkey". Those are just marketing terms that represent more less clearly defined configurations of the technologies described so far. Maybe a more interesting development is to also skip usernames. This is where we come back to discoverable credentials: Many authenticators have limited storage capacity, so they mainly keep a single secret. The private keys are encrypted using that secret and stored with the relying party. Now if I want to log in, I have to send my username so the relying party can pass the correct encrypted key. It could also send me all the keys, but that is neither secure nor practical. If, on the other hand, the key is stored on the authenticator, all that is not necessary. The relying party would send a challenge, my authenticator would use the local key to generate a reply, and I would be logged in. (With this explanation, maybe go back and see if you can understand the paragraph I quoted above.) Now, this comes with some usability bumps. For one, the relying party would have to know that my authenticator supports discoverable credentials and provide a legacy login otherwise. I guess that is the main reason why we hardly see this in the wild. The other issue is that having multiple accounts at the same relying party becomes a bit more complicated. Either I have different authenticators, or the authenticator allows me to pick one, or the relying party receives all keys and asks me which one to use. ## Recovery Before ending this post, we also have to look into some potential issue. The first one is recovery. There are many ways to lose access to your account. Something you know may be forgotten, something you own may be stolen or just break, and even your fingerprints can be lost (e.g. by losing a finger). The common way to tackle this issue, in the terminology we established so far, is to require `n` factors, but provide more than `n` factors. The most common version of this is that you can send a "reset password" link to your email account. The two factors here are a password and access to an email account. It is tempting to think of this as a completely separate recovery flow. But in practice this just means that either factor is sufficient. And the security of your account depends on the weakest one. With multi factor authentication, the most common issue is that recovery collapses into a single factor, e.g. it is possible to reset all factors using the same side channel or recovery keys are stored in the same password manager as the password. If that is the case, you are not doing MFA and not gaining any security. So make sure that your recovery options are solid! ## Account enumeration attacks When we are thinking about password-less approaches we also have to consider some attacks that have been solved for passwords a long time ago. Early on in my career I learned that I should display the same warning on a failed login attempt regardless of whether the username exists or not. This is so that attackers cannot find out whether a specific user has an account. In WebAuthn (and if not using discoverable credentials) the relying party will usually respond with a set of keys for a given username. How can we avoid enumeration in that case? The [spec](https://www.w3.org/TR/webauthn/#sctn-username-enumeration) recommends creating a fake key in that case, but that sounds like there could be all kinds of footguns. ## Conclusion After reading and hearing a lot about MFA, I had to structure all that information. I believe the idea to think of WebAuthn as local SSO can be productive. Even though MFA has come a long way, it is still too soon to say what kind of authentication we will settle on.