Jump to content
netcarver

2-factor authentication module

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

Share this post


Link to post
Share on other sites

Thanks to Soma for helping me with module questions via the #processwire IRC channel.

  • Like 1

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

I get the same errors with the additional line. For now I will try delaying the call to save() on the templates till all the fields are removed and see what I get.

Share this post


Link to post
Share on other sites

Ok, delaying calling save() on the fieldgroups until all fields have been removed works. Thanks for the tip Ryan.

  • Like 1

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Thanks Steve and sorry I missed the inline doc there (doh...). Cheers, -Alan

Share this post


Link to post
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

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Just pushed some updates to this ancient module. The new version (v1.2.0) uses wireMail and should work with PW 3.

  • Like 8

Share this post


Link to post
Share on other sites

any updates for php 7.2+ users ?

mcrypt is no longer supported in new versions of PHP (since 7.2) so this module cannot work due to the mcrypt dependency

http://php.net/manual/en/migration71.deprecated.php

mcrypt has been replaced with OpenSSL

I am trying to use the URL shortener module but it requires this module to work

Share this post


Link to post
Share on other sites

@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

Share this post


Link to post
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

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By bernhard
      --- Please use RockFinder3 ---
    • By MoritzLost
      Cacheable Placeholders
      This module allows you to have pieces of dynamic content inside cached output. This aims to solve the common problem of having a mostly cacheable site, but with pieces of dynamic output here and there.  Consider this simple example, where you want to output a custom greeting to the current user:
      <h1>Good morning, <?= ucfirst($user->name) ?></h1> This snippet means you can't use the template cache (at least for logged-in users), because each user has a different name. Even if 99% of your output is static, you can only cache the pieces that you know won't include this personal greeting. A more common example would be CSRF tokens for HTML forms - those need to be unique by definition, so you can't cache the form wholesale.
      This module solves this problem by introducing cacheable placeholders - small placeholder tokens that get replaced during every request. The replacement is done inside a Page::render hook so it runs during every request, even if the response is served from the template cache. So you can use something like this:
      <h1>Good morning, {{{greeting}}}</h1> Replacement tokens are defined with a callback function that produces the appropriate output and added to the module through a simple hook:
      // site/ready.php wire()->addHookAfter('CachePlaceholders::getTokens', function (HookEvent $e) { $tokens = $e->return; $tokens['greeting'] = [ 'callback' => function (array $tokenData) { return ucfirst(wire('user')->name); } ]; $e->return = $tokens; }); Tokens can also include parameters that are parsed and passed to the callback function. There are more fully annotated examples and step-by-step instructions in the README on Github!
      Features
      A simple and fast token parser that calls the appropriate callback and runs automatically. Tokens may include multiple named or positional parameters, as well as multi-value parameters. A manual mode that allows you to replace tokens in custom pieces of cached content (useful if you're using the $cache API). Some built-in tokens for common use-cases: CSRF-Tokens, replacing values from superglobals and producing random hexadecimal strings. The token format is completely customizable, all delimiters can be changed to avoid collisions with existing tag parsers or template languages. Links
      Github Repository & documentation Module directory (pending approval) If you are interested in learning more, the README is very extensive, with more usage examples, code samples and usage instructions!
    • By Craig
      I've been using Fathom Analytics for a while now and on a growing number of sites, so thought it was about time there was a PW module for it.
      WayFathomAnalytics
      WayFathomAnalytics is a group of modules which will allow you to view your Fathom Analytics dashboard in the PW admin panel and (optionally) automatically add and configure the tracking code on front-end pages.
      Links
      GitHub Readme & documentation Download Zip Modules directory Module settings screenshot What is Fathom Analytics?
      Fathom Analytics is a simple, privacy-focused website analytics tool for bloggers and businesses.

      Stop scrolling through pages of reports and collecting gobs of personal data about your visitors, both of which you probably don't need. Fathom is a simple and private website analytics platform that lets you focus on what's important: your business.
      Privacy focused Fast-loading dashboards, all data is on a single screen Easy to get what you need, no training required Unlimited email reports Private or public dashboard sharing Cookie notices not required (it doesn't use cookies or collect personal data) Displays: top content, top referrers, top goals and more
    • By daniels
      This is a lightweight alternative to other newsletter & newsletter-subscription modules.
      You can find the Module in the Modules directory and on Github
      It can subscribe, update, unsubscribe & delete a user in a list in Mailchimp with MailChimp API 3.0. It does not provide any forms or validation, so you can feel free to use your own. To protect your users, it does not save any user data in logs or sends them to an admin.
      This module fits your needs if you...
      ...use Mailchimp as your newsletter / email-automation tool ...want to let users subscribe to your newsletter on your website ...want to use your own form, validation and messages (with or without the wire forms) ...don't want any personal user data saved in any way in your ProcessWire environment (cf. EU data regulation terms) ...like to subscribe, update, unsubscribe or delete users to/from different lists ...like the Mailchimp UI for creating / sending / reviewing email campaigns *I have only tested it with PHP 7.x so far, so use on owners risk
      EDIT:
      Since 0.0.4, instructions and changelog can be found in the README only. You can find it here  🙂
      If you have questions or like to contribute, just post a reply or create an issue or pr on github, thanks!
    • By MoritzLost
      Sorry for the convoluted title. I have a problem with Process modules that define a custom page using the page key through getModuleInfo (as demonstrated in this excellent tutorial by @bernhard). Those pages are created automatically when the module is installed. The problem is that the title of the page only gets set in the current language. That's not a problem if the current language (language of the superuser who is installing the module) is the default language; if it isn't, the Process page is missing a title in the default language. This has the very awkward effect that a user using the backend in the default language (or any other language) will see an empty entry in the setup menu:

      This screenshot comes from my Cache Control module which includes a Process page. Now I realize the description sounds obscure, but for us it's a common setup: We a multiple bilingual sites where the default language is German and the second language is English. While the clients use the CMS in German, as a developer I prefer the English interface, so whenever I install a Process module I get this problem.
      As a module author, is there a way to handle this situation? I guess it would be possible to use post-installation hooks or create the pages manually, but I very much prefer the declarative approach. The page title is already translatable (through the __ function), but of course at the time of installation there is no translation, and as far as I'm aware it's not possible to ship translations with a module so they are used automatically. Could this situation be handled better in the core? I would prefer if the module installation process would always set the title of the Process page in the default language, instead of the language of the current user.
×
×
  • Create New...