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

  On 9/19/2012 at 1:25 PM, ryan said:

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.

  On 9/19/2012 at 1:25 PM, ryan said:

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.

  On 9/19/2012 at 1:25 PM, ryan said:

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

  Quote
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,

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

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

  Quote
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

  Quote
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):

  Quote

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

×
×
  • Create New...