Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-Factor Authentication (replaces Two Factor Authentication) #37912

Merged
merged 262 commits into from
Jun 4, 2022
Merged

Multi-Factor Authentication (replaces Two Factor Authentication) #37912

merged 262 commits into from
Jun 4, 2022

Conversation

nikosdion
Copy link
Contributor

Improved Two Factor Authentication

Replaces #37811

ATTENTION TESTERS

This PR replaces plugins and has changes in the JavaScript. If you are not using a prebuilt package you need to do the following:

  • Run composer install
  • Run npm ci
  • Delete the file administrator/cache/autoload_psr4.php

Executive Summary

This PR implements Multi-Factor Authentication (MFA) using a “Captive Login” approach.

Unlike the temporary Two Factor Authentication solution that was supposed to last for a maximum of one year and we've been using until today (nine years later...), it does NOT require you to enter your Security Code with your login information (e.g. username and password). In fact, there is no longer a Security Code field at all in the login module or page. Instead, you login using whichever login method you want to use. Then you enter the “captive login” mode in Joomla where you can only interact with a subset of com_users: you can proceed with MFA validation, choose a different MFA method or log out. Non-HTML views are forbidden.

Tagging @SniperSister @richard67 who were involved in the original discussion in the context of improving Joomla's security, @zero-24 @bembelimen @roland-d @HLeithner as the current and future release leads that need to be in the loop, @Kostelano and @heelc29 who participated in the previous PR.

Extra features implemented and considerations made

I implemented a few extra features to the old TFA since I already had the code for them and it'd be a waste to remove it only for someone else to try and replicate them in the future. I also tried to be very considerate of the security of the solution, keeping it in a fine balance with practicality. Without further ado, here's what I did.

Multiple MFA methods per user. There is no good reason why a user shouldn't have multiple MFA methods. In fact, it is a better approach e.g. having two WebAuthn keys (main and backup) and a classic six-digit code for use in an older smartphone which doesn't support WebAuthn dongles. This makes MFA more usable and more resilient to accidents that lock people out of their site.

Default method. If you have multiple authentication methods you don't want to stop and think which method you will be using to authenticate today, especially since you're likely to use the same method 99% of the time. Therefore you can pick which method will be your default. You can NOT use emergency codes as the default method to reduce the probability of silly mistakes.

Methods batching. Let's say you set up three WebAuthn authenticators. When you log in you want to see a single page asking you for a WebAuthn login, not having to click to select which WebAuthn authenticator you will be using (reduces the PICNIC errors — Problem In Chair, Not In Computer). I call that method batching because you can batch process all instances of a MFA method using a single input. WebAuthn and YubiKey let you set up multiple instances of that method so, indeed, they support method batching. Regular Authentication Code and Authentication Code by Email only allow a single instance so they don't. As simple as that.

Automatic migration of legacy data. To support multiple MFA methods per user we moved the MFA data from the two #__users columns (otpKey and otep) to its own table (#__user_tfa). Any already set up legacy TFA method is automatically migrated upon first login. This means that MFA will migrate cleanly even on sites with hundreds of thousands of users, something which would NOT be the case had we performed the migration during update (we'd timeout as we have to load each record individually, decrypt it, convert it, re-encrypt it and save it). Important: Until a user goes through a login you will NOT see their TFA/MFA status or configuration settings in com_users in the backend.

Data is encrypted. As alluded above, the MFA configuration data is still encrypted using AES-256 using a key derived from the site's secret. Therefore a simple SQLi vuln in any extension won't divulge the secrets for MFA.

New MFA methods. You can now use Authentication Code by Email and WebAuth on top of the existing Authentication Code and YubiKey methods. The former sends you a 6-digit code to your email and can also be set up to be forcibly enabled for anyone (to provide a viable backup if your users are not very tech savvy). The latter supports Windows Hello and Android, bringing in the improvements from my other PR (#37675).

Forced MFA and Forbidden MFA groups. You can optionally set up certain user groups to have MFA forcibly enabled and other groups to have MFA forcibly denied. The former will have to set up MFA and use it to continue using the site. The latter will never be asked to go through MFA, even if they had previously enabled it, nor will they see the MFA configuration section in com_users.

Onboarding to drive adoption. You can optionally have users (who do not belong in the forbidden MFA groups!) be automatically shown an onboarding page if they log in and they have not yet enabled MFA. They can set up MFA, go to a different page or forever dismiss the onboarding page. You can also customise the onboarding URL, e.g. if you want to display your own article instead of showing the default MFA setup page. According to my experience this increases MFA adoption by a factor of ten. Note that onboarding is DISABLED by default; we don't want people coming at us with torches and pitchforks for ruining their site's login experience.

Super Users cannot edit other Super Users. As a Super User you cannot touch another Super User's MFA settings at all. Consequently, if you mess up your MFA as a Super User you need to do some database editing. There's only so much we can do.

Administrators can only remove TFA options for other users. As a privileged user you can only remove MFA options from other (non-Super Users) user accounts, or disable their MFA completely. You cannot edit their TFA configuration or enrol new MFA methods.

Single click disable. The MFA configuration interface includes a Turn Off button which disables MFA completely for your user account. This is useful if you lost access to all MFA methods, you have used an emergency code and you just want to turn the darned thing off until you sort out your other problems.

Post-installation message. The old PIM for MFA has been removed and a new one has taken its place.

Module positions customisation in the captive page. You can choose which module positions to display in the captive MFA page. This lets you display, for example, your site's header and footer to give users a better visual experience.

No MFA on silent logins. By default, MFA is disabled on silent logins. You can toggle that as well as customise which login methods are to be considered a "silent" log in. By default these are the Cookie Authentication (remember me) and Passwordless (WebAuthn). The latter needs some explaining. WebAuthn is phishing-resistant and insanely secure — we only ever store a public key in the database, even if it's stolen it cannot be used to log you in (a stolen password hash can be brute forced MUCH more easily, by several orders of magnitude). This means that WebAuthn is strong enough to NOT require Multi-factor Authentication. That's the whole point of WebAuthn; replace usernames and passwords and the need for multifactor authentication with One Authentication Method To Rule Them All And In Security Bind Them.

Implementation notes

This PR is based on my past work on Akeeba LoginGuard, in active development since 2016. Back in late 2013 when I contributed the Two Factor Authentication feature we had agreed that entering the security code with the username and password was a bad idea but we only intended it as a temporary solution until Joomla 4.0 would be released in late-2014 to early-2015. Obviously the release plan changed and we got stuck with the old TFA method for nine years. Meanwhile, LoginGuard proved that my original idea of a captive login is feasible even without core changes and that code pattern found its way into later Joomla 3 and 4 versions e.g. com_privacy's plugin to provide consent and requiring a password reset.

This being a core PR instead of an extension I no longer have to use a weird system plugin to make the captive login work. I also didn't want to repeat the past mistakes of sprinkling magic code all over the darned place to integrate MFA to Joomla. So I used a Trait instead. Only the SiteApplication and AdministratorApplication use it. This is deliberate.

The CLI application does not have any authentication to speak of, nor it needs to. If you are in a position to either access a shell or define CRON jobs you have more control on the hosting environment than a Super User.

The Api application on the other hand is non-interactive. That's why it has a token authentication instead of relying on a username and password (see #27021 and the handy comic panel that drives the point home in #26925).

So, if you end up writing an interactive custom application you can use the Trait. If not, it's cool, DON'T integrate MFA — you actually MUST NOT.

Missing odds and ends

I have not integrated this with Joomla's Action Log. This is deliberate as there's currently an open PR (#37788) touching it and I don't want to create a Merge Conflict Hell.

Integration with Action Log should be a separate PR after this one is merged. Let's learn to walk before we learn to run.

Nicholas K. Dionysopoulos added 30 commits May 14, 2022 11:50
Import YubiKey plugin
Prepare SQL for new plugins
Import Fixed plugin (EXAMPLE)
System plugin
Replace the two factor authentication integration in the core
Fix wrong SQL / table name
Use correct prefix in the TFA helper when getting config UI
Fix a whoopsie or four
Coffee has long stopped working
Format the Methods page
Fix wrong TFA method internal name
Make sure we get the right view in the controllers
Remove yet another integration of the legacy TFA
Automatic migration from old TFA upon first login
Frontend MVC
Frontend routing
Style the method select page
Missed a legacy integration which needs removal
Better format of the configuration UI in the profile page
Use language strings when migrating data from legacy TFA
Only show the prompt to add a TFA method if none is already added
YubiKey should allow entry batching

This means that you can authenticate with any registered
YubiKey in your user profile.
Replace Tfa::triggerEvent
Import WebAuthn plugin
Improve TFA behavior on non-HTML pages. Basically, block
them!
Replace alerts with Joomla messages
Move onUserAfterDelete code to the `joomla` user plugin
Remove the System - Two Factor Authentication plugin

Use a trait for the application and fold the rest of
the code into Joomla's core user plugin.
@Kostelano
Copy link
Contributor

@nikosdion It seems that you planned to make a change to the issue #37912 (comment) (did not see the commit and can not check in reality on a PC).

@nikosdion
Copy link
Contributor Author

@Kostelano We can add this after the merge of this PR. We already have the language strings for this feature, it's just adding it to the output.

@Kostelano
Copy link
Contributor

I have tested this item ✅ successfully on 82ef6a1


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/37912.

@richard67
Copy link
Member

RTC


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/37912.

@joomla-cms-bot joomla-cms-bot added the RTC This Pull Request is Ready To Commit label Jun 3, 2022
@roland-d roland-d merged commit 186c21b into joomla:4.2-dev Jun 4, 2022
@joomla-cms-bot joomla-cms-bot removed the RTC This Pull Request is Ready To Commit label Jun 4, 2022
@roland-d
Copy link
Contributor

roland-d commented Jun 4, 2022

Thanks everybody for this great feature.

@roland-d roland-d added this to the Joomla 4.2.0 milestone Jun 4, 2022
@nikosdion
Copy link
Contributor Author

Thank you for the merge!

@richard67
Copy link
Member

@nikosdion It seems that you planned to make a change to the issue #37912 (comment) (did not see the commit and can not check in reality on a PC).

Who wants to make a PR? @Kostelano ? Or @nikosdion ?

@brianteeman
Copy link
Contributor

it si the same with other methods
image

@heelc29
Copy link
Contributor

heelc29 commented Jun 4, 2022

@roland-d Please add Language Change label

@richard67 richard67 added the Language Change This is for Translators label Jun 4, 2022
@roland-d
Copy link
Contributor

roland-d commented Jun 4, 2022

Thank you @richard67

@Kostelano
Copy link
Contributor

@nikosdion, give me a couple of free minutes.

I can't figure out the parameter "Force Enable" for plugin Authentication Code by Email.

I carefully read the description, tried to implement the situation in this way: I created a user, created an authentication method for him (fixed password), then enabled the above parameter in plugin Authentication Code by Email. I'm trying to log in, I switch to an alternative method (suppose I forgot the fixed code and did not save the emergency passwords), but I do NOT have a "forced method" (authentication code by mail).

What am I doing wrong, or perhaps I misinterpreted the parameter description? Thank you in advance for your response.

Should I automatically add the Authentication Code by Email as an option for all users? Useful to provide a fallback to users who have lost access to their main authenticator and haven't kept a copy of the backup codes at the expense of some degree of control and security.

@nikosdion
Copy link
Contributor Author

@Kostelano There were two missing lines not copied over from LoginGuard. Thank you for the heads up! I submitted #38029 to fix that.

fancyFranci pushed a commit that referenced this pull request Jun 18, 2022
* Add captive prompts for the rest of MFA methods

Addresses comment #37912 (comment)

Adds captive login prompts for these MFA methods:
* Backup Codes
* TOTP (Authenticator Code)
* YubiKey

The rest of the methods already had it:
* Email
* Fixed
* WebAuthn

* Update administrator/language/en-GB/com_users.ini

* Update language/en-GB/com_users.ini

Co-authored-by: Brian Teeman <brian@teeman.net>
roland-d added a commit that referenced this pull request Jun 18, 2022
Signed-off-by: Roland Dalmulder <contact@rolandd.com>
@Kostelano
Copy link
Contributor

I am not creating a bug because it does not pull on an error (probably), but I write here.

If we install some module separately, then with a high degree of probability we want to use it and it will still take its position in the template.

If we install a whole package, which may include a component/plugins/modules, then we won't necessarily use it all.

By default, the module occupies the NONE position. The same is immediately displayed in the parameters of the component users ---> MFA. Can we remove this position from the array of all positions for this reason?

@nikosdion

Screenshot_1

@nikosdion
Copy link
Contributor Author

@Kostelano I disagree. Sometimes you may want to create a module in the None position to display it in an article which in itself is displayed by a module in another, published position. If you disallow the None module position to be selected in this multi-select control that article would appear without the referenced module.

Remember, this only controls which modules will be loaded by Joomla (in fact, all modules are loaded and modules in module positions NOT listed in this control are forcibly UNLOADED using some black magic fuckery I am doing). It does not really control which module positions will be displayed.

@basd82
Copy link

basd82 commented Sep 6, 2022

screen shot 2022-09-06 at 14 56 44
Hi
I have the problem the allowed frontend/backend module positiions stay empty here


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/37912.

@brianteeman
Copy link
Contributor

@basd82 Please create a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Language Change This is for Translators
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants