Jump to content

2-factor authentication module


netcarver
 Share

Recommended Posts

I've been working on an experimental module set that adds 2-factor authentication to ProcessWire with the help of Steve Gibson's PPP one-time-pad system.

This is split into two modules; a CryptoPPP library that implements the otp system and a 2-factor authentication module that uses it to add 2-factor authentication to ProcessWire.

The 2-factor module adds an additional "Login Token" field to the login page into which the authenticating user will need to enter the next unused token from their one-time-pad. Pages from their pad can either be printed out in advance in a credit-card sized format (with codes being crossed out as they are used as shown here) or the required token can be sent to their registered email address so they don't need to print anything out. This second option requires a good email address be present in the user's account in order for them to be sent the token.

Email Delivery

To set up email delivery go to the 2-factor module's config page and choose "token delivery via email" and save the settings. Next, make sure that every user who will use the system has a valid email address set up in their account. Upon the first failed user login attempt, the required token will be emailed to the user’s email address and they should then be able to log in.

Printing Pages From The Pad

If you prefer to print the tokens in a handy credit-card sized format then…

  • Go to your profile screen
  • Expand the “PPP Initialisation Vector” field
  • Hit the “Show Token Cards” button to open a new browser window with the next 3 useful cards
  • Use your browser’s print option to print these out
  • Trim them to size and store

...but make sure you print these out before you enable 2-factor authentication on your account. If you cross out your used codes, you will always know which code to use when logging back in -- and if you forget, the first login attempt will fail and the token field will then prompt you with the location of the correct code to use.

Why would I ever want to use 2-factor authentication?

If your site is only for you or for people you know use good passwords then you probably never will need a 2-factor authentication system. But it has been shown that many users use passwords that are, well, rubbish not very good and having a second factor can be useful in mitigating poor passwords. As the second factor in this system comes out of a one-time-pad system (meaning it will not be reused) then having the user's password leaked or guessed should not compromise their account nor will having someone spy out the token they are using to log-in as tokens are not re-used (well, not for a very long time.)

Known Problems

  • You need to hit the save button after you install the 2-factor module to get it to remember the initial settings. (I guess I'm not setting the defaults correctly at present but pressing the button will allow you to move forward for now)
  • Uninstall of the 2-factor module leads to a lot of warnings.

Attachments

post-465-0-23222100-1348057034_thumb.png

post-465-0-32564900-1348057199_thumb.png

  • Like 10
Link to comment
Share on other sites

This is really awesome Steve, and a great idea. Just tested out here and it works great! Thanks for your work with this module, it looks really well put together.

One question: why is CryptoPPP.module kept separate? Just wondering if this might be simpler for people to install if they could just download 1 zip (or 1 clone) and have everything needed in 1 directory. You could add CryptoPPP to your getModuleInfo 'installs' line, and have it install and/or uninstall automatically with Auth2FactorPPP (ProcessWire will do this for you have you use that 'installs' property). Though if you are planning to make other modules that use CryptoPPP, then I suppose it makes sense to keep them separate. But just wanted to check.

I would love to see this in the modules directory if you have time to add it: http://modules.processwire.com/add/ … thanks again for the great work you've done with these modules!

  • Like 1
Link to comment
Share on other sites

This is really awesome Steve, and a great idea. Just tested out here and it works great!

That's good to know Ryan, thanks for testing it.

One question: why is CryptoPPP.module kept separate? Just wondering if this might be simpler for people to install if they could just download 1 zip (or 1 clone) and have everything needed in 1 directory. You could add CryptoPPP to your getModuleInfo 'installs' line, and have it install and/or uninstall automatically with Auth2FactorPPP (ProcessWire will do this for you have you use that 'installs' property). Though if you are planning to make other modules that use CryptoPPP, then I suppose it makes sense to keep them separate. But just wanted to check.

I wasn't aware of the installs property, must have missed that from my reading of the wiki.

I separated it in case anyone else wanted to develop modules that use the PPP one-time-pad system (though I don't have any further plans to right now) and also because the code in CryptoPPP is mainly comprised of parts of existing PHP implementations of the PPP system and I wanted to able to credit those folks in the module's information. My contributions are mainly within the 2-factor authentication module.

I would love to see this in the modules directory if you have time to add it: http://modules.processwire.com/add/ … thanks again for the great work you've done with these modules!

I plan on doing that once there has been some additional testing and I've worked out the two problems I have with it at present (see the Known Problems part of the opening post.)

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Hello, can anybody help me work out why this module's uninstall code doesn't seem to work cleanly?

I define an array of fields that the module creates and then adds into the user template on install. However, when reversing this in the uninstall() method I get multiple errors.

I must have missed something; the uninstall seems to be doing the reverse of the install routine (to me) yet I get multiple errors saying "Unable to delete from 'field_ppp_login_enable' for field that doesn't exist in fields table" even though that field only undergoes a single delete() call -- and, unless removing() it from the template's fieldgroup also deletes it -- it should exist at the point delete() is called.

Ideas?

Link to comment
Share on other sites

I think I understand what's going on, though haven't stepped through the live code yet. But it looks to me like there's potentially an issue in PW's Fieldgroups::save function where it doesn't like to be called multiple times. It queues fields for removal and doesn't do the actual remove until you call Fieldgroup::save. That's what it should do. But as far as I can tell, Fieldgroups::save doesn't clear out the removal queue after it completes the save, meaning it may try to remove something that's already been removed if you are making multiple calls to it. Assuming I'm right about that, it's an easy fix to the core (and already made it locally), but for compatibility with current versions, try making this change:

after your $t->fieldgroup->save(); in uninstallField add this line:

$t->fieldgroup->removedFields->remove("name=$name"); 

does that fix it?

  • Like 1
Link to comment
Share on other sites

  • 5 months later...

Steve, this is a way cool Module, thanks so much :D

I am (once again (no surprises here)) a total newb at PPP so I wonder if I might ask you a quick question? I would like to

To create tokens with a format such as ‘X7gM-jA9v-KtWs’

as you say in github. Using this code

$key = CryptoPPP::genKeys();
$key = CryptoPPP::keyToTokenBlocks($key);

I am getting this sort of output

mEq:-U#nJ

What do I need to do to adjust the number of characters and also to ensure I get codes like the example you have (ones that will be safe to use as GET variables such as X7gM-jA9v-KtWs)?

Thanks very much in advance for any comments.

PS: I have no clue how I am going to do it (yet) but I am hoping to use this as part of a verification process so when someone makes a booking, they are sent an email with a link back to example.com/check-the-email/X7gM-jA9v-KtWs and if the X7gM-jA9v-KtWs is found then we know they got the code in the first place so we are happy and can continue. I think I know the steps I want but not done the code yet; exciting!

Link to comment
Share on other sites

Hi Alan,

Try something like this...

$key = CryptoPPP::genKeys();
$key = CryptoPPP::keyToTokenBlocks($key, 4, 3, '-', CryptoPPP::DISTINCT_CHARSET);

That's off the top of my head and untested. You should get three blocks of four chars from the "Distinct charset" (which should be safe as a GET variable) with a dash between the blocks.

You can check the parameters of keyToTokenBlocks here, and here are the characters from the "distinct charset".

  • Like 1
Link to comment
Share on other sites

Hey Alan,

No probs re the docs.

Something you might want to consider: Reducing the number of characters in the output character set (by using the distinct charset) does reduce the strength of the generated token somewhat so you may want to consider increasing the length of your generated tokens to compensate for this -- maybe by using 4 runs of 4 chars.

  • Like 1
Link to comment
Share on other sites

Thanks for the tip Steve, yeah I'd already gone off like fox in a chicken coop and so far was playing with 6 6 (it's addictive just seeing those lovely long random strings) :D Doing something else right now but really looking forward to coming back to this and tackling implementing it for real.

Link to comment
Share on other sites

  • 5 months later...

Hi Steve/chums,

Sorry to type-n-go but late and wanted to ask this quickly in case it's easy (hoping ;))

I have just put a site up that was running aok here on MAMP and have hit a PHP dependency (I think) that it looks like my host does not (yet) provide but MAMP does.

When I go to a page that uses this brilliant crypto module locally, all AOK, on the host I get:

Fatal error: Call to undefined function bcmul() in /var/www/.../site/modules/CryptoPPP/CryptoPPP.module on line 78
This error message was shown because site is in debug mode ($config->debug = true; in /site/config.php). Error has been logged.

I did a phpinfo to see if bcmul is something I can turn on in php.ini but seems not.

This seems to suggest a solution.

Any pointers as how to best solve this? Thank you VERY much indeed for any help! Cheers, -Alan

Link to comment
Share on other sites

Hi Alan,

Really busy here at the moment so sorry for the brief reply but, yes, the "bc" maths functions are needed for the PPP library. I thought I had the modules check for availability of those functions when it was installed (will check later) did you clone your site to your host (in which case modules will not execute the prerequisite function availability check) or did you try installing the modules directly on the host?

Edited by netcarver
  • Like 1
Link to comment
Share on other sites

Hi Steve,

Really busy here at the moment so sorry for the brief reply

NO problem! I don't have a SLA with you :)

did you clone your site to your host (modules will not execute prerequisite code) or did you try installing the modules directly on the host?

I did clone. I will try to uninstall and reinstall and post back (but if I understand this right it won't help, but will do anyway just in case).

Thanks VERY much for replying at all when you are busy and no expectations here for any speed of next reply, thanks again! Cheers, -Alan

Link to comment
Share on other sites

Hi Steve,

Uninstall and re-install does helpfully say

Unable to install module 'CryptoPPP': bcadd not available

so I will go talk to my hoster to see if I can have this added. I am guessing they will say my PHP build is locked down and I can't (bless).

Assuming I can't, as/when you are able, do you know off hand if that idea/alt code in that link might help me? Tots understand if you have no time to answer. Cheers, -Alan

Link to comment
Share on other sites

Update: to get me out of a hole I am using an alternative solution; so no worries. All I was using CryptoPPP for was to get a lovely one-time random string (so I was not using it to do much) and my solution was the following, I needed it to provide a degree of unpredictable-ness so humans could not guess it and I needed to ensure it was unique, hence the use of a rand fn + date stamp:

// Based on http://stackoverflow.com/a/4356295/236306 plus a mod of mine, namely I tack the output of time() on the end, see inline comment below
function crypto($length = 6) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, strlen($characters) - 1)];
    }
    // Right up until here the code is not mine, the line below is edited by me to tack a unix timestamp on the end
    return $randomString . time();
}

This returns this sort of thing each time it's run, if for example you run it once per second (which I don't):

EoiMrQ1379520972

7p4b4r1379520973

8GyhxU1379520974

Link to comment
Share on other sites

  • 3 years later...
  • 1 year later...
  • 2 months later...

@neosin

Sorry for the slow response to this, but I just updated both the 2-Factor authentication module (for PW3+) and the CryptoPPP library module (switched it to OpenSSL.) If you still want to, try grabbing the latest version 3.1.1 and that should work under PHP 7.2.

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Robin S
      This module lets you add some custom menu items to the main admin menu, and you can set the dropdown links dynamically in a hook if needed.
      Sidenote: the module config uses some repeatable/sortable rows for the child link settings, similar to the ProFields Table interface. The data gets saved as JSON in a hidden textarea field. Might be interesting to other module developers?
      Custom Admin Menus
      Adds up to three custom menu items with optional dropdowns to the main admin menu.
      The menu items can link to admin pages, front-end pages, or pages on external websites.
      The links can be set to open in a new browser tab, and child links in the dropdown can be given an icon.
      Requires ProcessWire v3.0.178 or newer.
      Screenshots
      Example of menu items

      Module config for the menus

      Link list shown when parent menu item is not given a URL

      Advanced
      Setting child menu items dynamically
      If needed you can set the child menu items dynamically using a hook.
      Example:
      $wire->addHookAfter('CustomAdminMenus::getMenuChildren', function(HookEvent $event) { // The menu number is the first argument $menu_number = $event->arguments(0); if($menu_number === 1) { $colours = $event->wire()->pages->findRaw('template=colour', ['title', 'url', 'page_icon']); $children = []; foreach($colours as $colour) { // Each child item should be an array with the following keys $children[] = [ 'icon' => $colour['page_icon'], 'label' => $colour['title'], 'url' => $colour['url'], 'newtab' => false, ]; } $event->return = $children; } }); Create multiple levels of flyout menus
      It's also possible to create multiple levels of flyout submenus using a hook.

      For each level a submenu can be defined in a "children" item. Example:
      $wire->addHookAfter('CustomAdminMenus::getMenuChildren', function(HookEvent $event) { // The menu number is the first argument $menu_number = $event->arguments(0); if($menu_number === 1) { $children = [ [ 'icon' => 'adjust', 'label' => 'One', 'url' => '/one/', 'newtab' => false, ], [ 'icon' => 'anchor', 'label' => 'Two', 'url' => '/two/', 'newtab' => false, 'children' => [ [ 'icon' => 'child', 'label' => 'Red', 'url' => '/red/', 'newtab' => false, ], [ 'icon' => 'bullhorn', 'label' => 'Green', 'url' => '/green/', 'newtab' => false, 'children' => [ [ 'icon' => 'wifi', 'label' => 'Small', 'url' => '/small/', 'newtab' => true, ], [ 'icon' => 'codepen', 'label' => 'Medium', 'url' => '/medium/', 'newtab' => false, ], [ 'icon' => 'cogs', 'label' => 'Large', 'url' => '/large/', 'newtab' => false, ], ] ], [ 'icon' => 'futbol-o', 'label' => 'Blue', 'url' => '/blue/', 'newtab' => true, ], ] ], [ 'icon' => 'hand-o-left', 'label' => 'Three', 'url' => '/three/', 'newtab' => false, ], ]; $event->return = $children; } }); Showing/hiding menus according to user role
      You can determine which menu items can be seen by a role by checking the user's role in the hook.
      For example, if a user has or lacks a role you could include different child menu items in the hook return value. Or if you want to conditionally hide a custom menu altogether you can set the return value to false. Example:
      $wire->addHookAfter('CustomAdminMenus::getMenuChildren', function(HookEvent $event) { // The menu number is the first argument $menu_number = $event->arguments(0); $user = $event->wire()->user; // For custom menu number 1... if($menu_number === 1) { // ...if user does not have some particular role... if(!$user->hasRole('foo')) { // ...do not show the menu $event->return = false; } } });  
      https://github.com/Toutouwai/CustomAdminMenus
      https://processwire.com/modules/custom-admin-menus/
    • By tcnet
      This module for ProcessWire sends a notification email for each failed login attempt. Similar modules exists already in the module directory of ProcessWire. However, this module is designed to notify, even if specified user doesn't exist.
      Settings
      The settings for this module are located in the menu Modules=>Configure=>LoginFailNotifier.
      Notification email
      Specifies the email address to which the notification emails should be sent.
        Email subject
      Specifies the subject line for the notification email.
        Post variables
      Specifies the $_POST variables to be included in the notification email. Each variable must be separated by a comma. For example: login_name,login_pass
        Server variables
      Specifies the $_SERVER variables to be included in the notification email. Each variable must be separated by a comma. For example: REMOTE_ADDR,HTTP_USER_AGENT
      Link to ProcessWire module directory:
      https://processwire.com/modules/login-fail-notifier/
      Link to github.com:
      https://github.com/techcnet/LoginFailNotifier
    • By Fokke
      ProcessWire 3.x markup module for rendering meta tags in HTML document head section. Note that this module is not a full-blown SEO solution, but rather a simple tool for rendering meta tags based on module configuration. Adding custom meta tags is also supported.
      Built-in meta tags
      The following meta tags are supported out-of-the-box:
      Document title consisting of page title and site name Character set Canonical Viewport Description Keywords Hreflang tags Open Graph og:title og:site_name og:type og:url og:description og:image og:image:width og:image:height Twitter meta tags twitter:card twitter:site twitter:creator twitter:title twitter:description twitter:image Facebook meta tags fb:app_id The full documentation with configurable options can be found here: https://github.com/Fokke-/MarkupMetadata
       
      Requirements:
      ProcessWire>=3.0.0 PHP >=7.1 Installation using Composer
      composer require fokke/markup-metadata Manual installation
      Download latest version from https://github.com/Fokke-/MarkupMetadata/archive/master.zip Extract module files to site/modules/MarkupMetadata directory.
    • By m.sieber
      ITRK-Service for ProcessWire
      Module for the automated transfer of imprint, data protection declaration and terms and conditions from IT-Recht Kanzlei to your ProcessWire installation
      What is ITRK Service for ProcessWire?
      ITRK-Service for ProcessWire is a free module for ProcessWire CMS. It provides an interface to the update service of IT-Recht Kanzlei, via which the legal texts of your online presence are automatically updated. In this way, the texts remain legally secure and warning-proof in the long term. Imprint, data protection declaration, revocation and general terms and conditions are currently supported.
      You can find our documentation (in german language) here: https://www.pupit.de/itrk-service-for-processwire/dokumentation/

      Download: https://www.pupit.de/itrk-service-for-processwire/
      Github: https://github.com/pupit-de/pwItrkServiceConnector
    • By LuisM
      Symprowire is a PHP MVC Framework based and built on Symfony using ProcessWire 3.x as DBAL and Service-Provider
      It acts as a Drop-In Replacement Module to handle the Request/Response outside the ProcessWire Admin. Even tough Symfony or any other mature MVC Framework could be intimidating at first, Symprowire tries to abstract Configuration and Symfony Internals away as much as possible to give you a quick start and lift the heavy work for you.
      The main Goal is to give an easy path to follow an MVC Approach during development with ProcessWire and open up the available eco-system.
      You can find the GitHub Repo and more Information here: https://github.com/Luis85/symprowire
      Documentation
      The Symprowire Wiki https://github.com/Luis85/symprowire/wiki How to create a simple Blog with Symprowire https://github.com/Luis85/symprowire/wiki/Symprowire-Blog-Tutorial Last Update
      16.07.2021 // RC 1 v0.6.0 centralized ProcessWire access trough out the Application by wrapping to a Service https://github.com/Luis85/symprowire/releases/tag/v0.6.0-rc-1 Requirements
      PHP ^7.4 Fresh ProcessWire ^3.0.181 with a Blank Profile Composer 2 (v1 should work, not recommended) The usual Symfony Requirements Features
      Twig Dependency Injection Monolog for Symprowire Support for .env YAML Configuration Symfony Console and Console Commands Symfony Webprofiler Full ProcessWire access inside your Controller and Services Webpack Encore support Caveats
      Symfony is no small Framework and will come with a price in terms of Memory Usage and added Overhead. To give you a taste I installed Tracy Debugger alongside to compare ProcessWire profiling with the included Symfony Webprofiler

      So in a fresh install Symprowire would atleast add another 2MB of Memory usage and around 40ms in response time, should be less in production due to the added overhead of the Webprofiler in dev env
       
×
×
  • Create New...