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

[RFC] two-factor authentication #1545

Merged
merged 50 commits into from
Jul 12, 2018
Merged

Conversation

bytehead
Copy link
Member

@bytehead bytehead commented Jun 9, 2018

This is a working draft to add a two-factor authentication mechanism 2FA with TOTP for backend users.
It uses scheb/two-factor-bundle to extend the symfony security authentication mechanism, spomky-labs/otphp for generating and validating one-time passwords and bacon/bacon-qr-code to render QR codes as SVGs.

Please review and mention all missed topics.

Changes to security.yml:

security:
    firewalls:
        contao_backend:
            two_factor:
                auth_form_path: contao_backend_two_factor
                check_path: contao_backend_two_factor_check
                auth_code_parameter_name: verify

    access_control:
        - { path: ^/contao/two-factor, roles: [IS_AUTHENTICATED_2FA_IN_PROGRESS, ROLE_USER] }

Changes to AppKernel.php:

/**
     * {@inheritdoc}
     */
    public function registerBundles()
    {
        $bundles = [
  			// ...
            new Scheb\TwoFactorBundle\SchebTwoFactorBundle(),
        ];
		// ...
    }

How to enforce 2FA for backend users via config.yml:

contao:
    authentication:
        two_factor:
            enforce_backend: true    #default: false

ToDos:

  • Move QR code image to user profile
  • Unit tests
  • Option to enforce 2FA for all backend users
  • Icon in the backend list view of tl_user indicating which users have 2FA enabled

Possible new features (future PRs):

  • 2FA also for frontend users

@leofeyer leofeyer added this to the 4.6.0 milestone Jun 9, 2018
@bytehead bytehead force-pushed the feature/two-factor branch 4 times, most recently from 935034f to 6203d91 Compare June 11, 2018 22:15
@richardhj
Copy link
Member

🙌

I'm really looking forward to see this feature.

  • You aren't using the Google Authenticator already part of scheb/two-factor-bundle because it is not that generic than the implementation of spomky-labs/otphp?
  • We might want an icon in the backend list view of tl_user indicating which users have 2FA enabled.
  • We might want to force users to use 2FA.
  • 2FA must only get activated after the user successfully confirmed the setup with a token generated on his 2FA device.

And currently im getting the Unrecognized option "two_factor" under "security.firewalls.contao_backend" error. Do I need to alter the configuration in the DI extension?

@bytehead
Copy link
Member Author

bytehead commented Jun 12, 2018

You aren't using the Google Authenticator already part of scheb/two-factor-bundle because it is not that generic than the implementation of spomky-labs/otphp?

Yes I don't use it. It didn't work with all of my apps I've tried upfront. That's why I decided to choose a library that supports actually the RFCs. Even easier, if we decide to support HOTP in future.

We might want an icon in the backend list view of tl_user indicating which users have 2FA enabled.

I like the idea, @contao/developers what do you think about?

We might want to force users to use 2FA.

IMHO the users should enable 2FA by themselves.

2FA must only get activated after the user successfully confirmed the setup with a token generated on his 2FA device.

I will implement this, already discussed 👍

And currently im getting the Unrecognized option "two_factor" under "security.firewalls.contao_backend" error. Do I need to alter the configuration in the DI extension?

Did you miss to enable the SchebTwoFactorBundle?

@leofeyer
Copy link
Member

We might want an icon in the backend list view of tl_user indicating which users have 2FA enabled.

I like the idea, @contao/developers what do you think about?

Go for it.

We might want to force users to use 2FA.

IMHO the users should enable 2FA by themselves.

I also think we should not force them.

@fritzmg
Copy link
Contributor

fritzmg commented Jun 13, 2018

We might want to force users to use 2FA.

IMHO the users should enable 2FA by themselves.

I also think we should not force them.

I think @richardhj meant that you should optionally be able to enforce 2FA for one Contao Installation for everyone. Not that it should be enforced in general. This makes sense imho.

@richardhj
Copy link
Member

I think @richardhj meant that you should optionally be able to enforce 2FA for one Contao Installation for everyone. Not that it should be enforced in general. This makes sense imho.

Optionally for some users or user groups, in order to comply with some TOMs :-)
Fore sure it is not necessary.

@bytehead
Copy link
Member Author

I think @richardhj meant that you should optionally be able to enforce 2FA for one Contao Installation for everyone. Not that it should be enforced in general. This makes sense imho.

I don't get this one.

If somebody else enforces 2FA in general the question will be how the users will initially setup the app in this case?

@aschempp
Copy link
Member

We might want an icon in the backend list view of tl_user indicating which users have 2FA enabled.

I like the idea, @contao/developers what do you think about?

I like it as well. Maybe a badge on the user/admin icon like the page publishing options?

If somebody else enforces 2FA in general the question will be how the users will initially setup the app in this case?

Something like forcing the setup after login attempt?

@leofeyer
Copy link
Member

Please don't do this. None of the apps I am using 2FA with does it – and I am using a lot of them.

@patrickjDE
Copy link
Contributor

As an option to enforce 2FA upon users would be, well.. optional, I don't see a problem there. It'd work analogous to "Password change required". Upon first/next login the user is required to change his password/setup 2FA, whatever the admin's reasons for that decision might be.

If 2FA becomes a core feature, so should the option to enforce its usage upon all or specific users.

@fritzmg
Copy link
Contributor

fritzmg commented Jun 14, 2018

I agree with @patrickjDE . A company should ideally be able to decide whether to force all editors of their website two use 2FA in order to increase security.

@leofeyer you are probably talking about a front end context there, right? But this is about the back end functionality. If your website has 2 administrators for the back end and only one uses 2FA, the other account is still (maybe) more vulnerable. Hence you might want to decide that every back end user (or specific back end user groups - or just administrators) should use 2FA.

@bytehead
Copy link
Member Author

We might want an icon in the backend list view of tl_user indicating which users have 2FA enabled.

@frontendschlampe do you want to create an icon for it? 😎

continue;
}

$GLOBALS['TL_DCA']['tl_user']['palettes'][$palette] = str_replace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the PaletteManipulator here? Would make things easier 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely! 😎

// Generate 1024 bit secret
$secret = random_bytes(128);

$this->Database->prepare("UPDATE tl_user SET secret=? WHERE id=?")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be something like this?

$user = BackendUser::getInstance();
$user->secret = random_bytes(128);
$user->save();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Can't see the forest for the trees.

<div class="' . $class . ' widget">
<div id="ctrl_' . $dc->field . '" class="">
<h3><label for="ctrl_' . $dc->field . '">' . $GLOBALS['TL_LANG']['tl_user']['2faQrCode'][0] . '</label></h3>
<img src="data:image/svg+xml;base64,' . base64_encode($twoFactorAuthenticator->getQrCode($this->User, $request)) . '" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use BackendUser::getInstance() everywhere instead of $this->User that's stored somewhere in nowhere?

throw new Exception($GLOBALS['TL_LANG']['ERR']['invalidTwoFactor']);
}

return 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imho true should work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

*/
public function validateCode(User $user, string $code): bool
{
// The 2FA app from Google (Google authenticator) does not strictly confirm to RFC 4648 [1] (they confirm to the old RFC 3548 [2]).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private function getUpperUnpaddedScretForUser(User $user): string

Then you need to add the google comment only once :-P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True that.

@frontendschlampe
Copy link
Contributor

@frontendschlampe do you want to create an icon for it?

yes ... :-) Today is icon day :-D

@frontendschlampe
Copy link
Contributor

What do you think?

2fa

@richardhj
Copy link
Member

Love it.
Should be on the left side since it is no action button though.

@bytehead
Copy link
Member Author

@frontendschlampe Yeah, I like! I'll place it on the left as @richardhj already stated.

@bytehead
Copy link
Member Author

Here is the implemented version:

bildschirmfoto 2018-06-20 um 07 33 06

@frontendschlampe
Copy link
Contributor

yeah ... no problem. I‘ve added the icon to my PR yesterday with the other icons. :-)

@aschempp
Copy link
Member

With the change in 4e36d00, so we actually still need a different route for the 2FA or can we just apply 2FA on the login route?

@bytehead
Copy link
Member Author

@aschempp see here: #1545 (comment)

return false;
}

if (!$this->enforceTwoFactor && !$user->useTwoFactor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if the user does not have a secret (which is the default, if he never edited his data), then we don't enforce 2FA?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems correct to me. Otherwise the user would be asked to enter their verification code although they never had a chance to set up 2FA.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact that's wrong. If there is no secret and 2FA is enforced, a secret should be generated, so the user gets the QR code to verify on the login screen.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in eac180e.

return false;
// Generate a secret if 2FA is enforced and the user has not yet enabled it
if ($this->enforceTwoFactor && !$user->secret) {
$user->secret = random_bytes(128);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't here a $user->save(); missing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK. Can we be sure that $context->getUser() returns an object with a save() method? Also it seems to work without save().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we can, it returns a Contao\User object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 9f3b5df.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member Author

@bytehead bytehead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

// 2FA is now confirmed, save the user flag
if ($this->enforceTwoFactor && !$user->confirmedTwoFactor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we only set this if 2FA is enforced?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this only happens if 2FA is enforced by config. Otherwise it gets saved here.

@leofeyer leofeyer merged commit b6ce152 into contao:master Jul 12, 2018
@leofeyer
Copy link
Member

Thank you big time @bytehead!

@bytehead bytehead deleted the feature/two-factor branch July 12, 2018 10:25
leofeyer pushed a commit that referenced this pull request Jul 13, 2018
Description
-----------

This is what I meant in #1545 (comment)

Commits
-------

3b35e23 Drop the unnecessary 2FA login route
2da1351 Also adjust the tests and the firewall and rename the check route.
2398caa Merge branch 'master' into feature/no-2fa-route
christian-kolb pushed a commit to christian-kolb/contao that referenced this pull request Oct 11, 2018
Description
-----------

- improve icon modules.svg
- add icons for contao/core-bundle#1202
- add icon for contao/core-bundle#1545

Commits
-------

abb6003b add/improve some icons
f14694e4 improve size of 2fa icons (same height like user icons)
d37484eb improve module icon again
a86c900d delete single 2fa-icons and add user/admin icon with lock
aeaf343b improve size units of some icons
3922030e improve position of icon elements
0c427ba5 Run the gulp task.
christian-kolb pushed a commit to christian-kolb/contao that referenced this pull request Oct 11, 2018
Description
-----------

This is what I meant in contao/core-bundle#1545 (comment)

Commits
-------

3b35e23d Drop the unnecessary 2FA login route
2da1351d Also adjust the tests and the firewall and rename the check route.
2398caa0 Merge branch 'master' into feature/no-2fa-route
leofeyer added a commit to contao/contao that referenced this pull request Jan 11, 2019
leofeyer added a commit that referenced this pull request Jan 11, 2019
@BoozieBadd
Copy link

#1545

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

Successfully merging this pull request may close these issues.

9 participants