Jump to content
twhiting9275

Require security question selection if empty & notify client on change

Recommended Posts

Another hook, but a pretty good one, really. Require your clients to enter in a security question if they have none in the system.

NOTE

Do not attempt this if you don't have security questions setup, it will just send you in a continual loop!

Place the following as a php file in whmcs/includes/hooks/

<?php

//redirect clients to security questions page if they don't have one setup. Nothing else.
//provided by https://www.whmcs.guru
use Illuminate\Database\Capsule\Manager as Capsule;

function check_client_security($vars)
{

//are we logged in? If not, then return
$uid  = $_SESSION['uid'];
if (empty($uid))
{
	return;
}

$displayTitle = $vars['displayTitle'];

$securityq = Capsule::table('tblclients')->where('id', '=', $uid)->pluck('securityqid');
if (is_array($securityq))
{
	$securityq = $securityq['0'];

}

if (empty($securityq))
{

	if ($displayTitle == "Security Settings")
	{
		//we are already here, assign the variable
		$extraVariables = [];
		$extraVariables['ctest'] = 'Please establish a security question for your account<br />This will be used to validate your account should we ever need to<br />';
		return $extraVariables;
	}
	header('Location: clientarea.php?action=security');
}
else
{
	return;
}
}

add_hook('ClientAreaPage', 1, "check_client_security");

 

Now, for the really paranoid, tell your clients when someone's changed their password, or security question. Save this as a php file in whmcs/includes/hooks/ as well

Make sure to replace YOURADMINHERE with your admin API user

<?php
/*
Client area Password and Security Question modification notification
Developed by http://www.whmcsguru.com
*/

use Illuminate\Database\Capsule\Manager as Capsule;
function hook_client_password_notify($vars)
{

$userid = $vars['userid'];
$filename = APP::getCurrentFileName();
$action = $_GET['action'];
$success = $_GET['success'];

if ($filename=='clientarea' && $action=='changepw') {
	send_password_notify($userid);
}
}
function check_security_change($vars)
{
$userid = $_SESSION['uid'];
$filename = $vars['filename'];
$action = $_GET['action'];
$success = $_GET['successful'];
if ($filename =="clientarea")
{
	if ($action == "changesq")
	{
		if ($success == "true")
		{
			send_security_notify($userid);
		}
	}
}
}
function send_password_notify($userid)
{
$ip = $_SERVER['REMOTE_ADDR'] ;
$hostname = gethostbyaddr($ip);
$userinfo = Capsule::table('tblclients')->select('firstname', 'lastname')->WHERE('id', $userid)->get();
//greet them
foreach ($userinfo as $userrow)
{
	$firstname = $userrow->firstname;
	$lastname = $userrow->lastname;
}
$command = "sendemail";
$adminuser = "YOURADMINHERE";
$values["customtype"] = "general";
$values["customsubject"] = "Password Modification from $hostname";
$values["custommessage"] = "<p>Hello $firstname $lastname,<p>A remote user successfully changed your password. If this was not you, please do contact us immediately<br /> You may contact us by replying directly to this email<p>IP Address: $ip<br/>Hostn
ame: $hostname<br />";
$values["id"] = $userid;

$results = localAPI($command, $values, $adminuser);
}
function send_security_notify($userid)
{
$ip = $_SERVER['REMOTE_ADDR'] ;
$hostname = gethostbyaddr($ip);
$userinfo = Capsule::table('tblclients')->select('firstname', 'lastname')->WHERE('id', $userid)->get();
//greet them
foreach ($userinfo as $userrow)
{
	$firstname = $userrow->firstname;
	$lastname = $userrow->lastname;
}
$command = "sendemail";
$adminuser = "YOURADMINHERE";
$values["customtype"] = "general";
$values["customsubject"] = "Security Question Modification from $hostname";
$values["custommessage"] = "<p>Hello $firstname $lastname,<p>A remote user successfully changed your security question. If this was not you, please do contact us immediately<br /> You may contact us by replying directly to this email<p>IP Address: $ip<
br/>Hostname: $hostname<br />";
$values["id"] = $userid;

$results = localAPI($command, $values, $adminuser);
}

add_hook('ClientChangePassword', 1, 'hook_client_password_notify');
add_hook('ClientAreaPage', 1, "check_security_change");

 

These hooks combined will let your clients know a bit more about what they've got going on.Always a good thing :)

Edited by twhiting9275

Share this post


Link to post
Share on other sites

Hi,

 

I have tried this hook, it directs to the security question page, but doesn't display the custom message, the page still has the default content.

Share this post


Link to post
Share on other sites

Looks like I forgot that part. For this, you need to edit the template (templates/yourtemplate/clientareasecurity.tpl)

After


{if $securityquestionsenabled && !$twofaactivation}

   <h2>{$LANG.clientareanavsecurityquestions}</h2>

add

  {if $ctest}
       {include file="$template/includes/alert.tpl" type="error" errorshtml=$ctest}

 

That will display the message

Unfortunately, no way to display the message without editing that file, but it's a very simple edit

Share this post


Link to post
Share on other sites

I am not really a fan of editing templates, as I have to remember to redo it every time I update whmcs.

 

would it possible instead to just popup an alert dialog when the user logs in telling them to set their security question, rather than automatically redirect them. Presumably this could be done with just a hook ?

Share this post


Link to post
Share on other sites

I have found a problem with this.

I often need to "login as client", but this hook stops me being able to do anything as I get redirected to the security page.

 

would be possible to just inject a JavaScript alert instead, telling them to set security question, rather than redirect to the security page.

Share this post


Link to post
Share on other sites

I often need to "login as client", but this hook stops me being able to do anything as I get redirected to the security page.

 

This will fix that:

 

<?php

//redirect clients to security questions page if they don't have one setup. Nothing else.
//provided by https://www.whmcs.guru
use Illuminate\Database\Capsule\Manager as Capsule;

function check_client_security($vars)
{

   //are we logged in? If not, then return
   $uid  = $_SESSION['uid'];
   if (empty($uid))
   {
       return;
   }

//check for admin userid
   $isadmin = $_SESSION['adminid'];
   if (!is_empty($isadmin))
   {
   return;
   }

   $displayTitle = $vars['displayTitle'];

   $securityq = Capsule::table('tblclients')->where('id', '=', $uid)->pluck('securityqid');
   if (is_array($securityq))
   {
       $securityq = $securityq['0'];

   }

   if (empty($securityq))
   {

       if ($displayTitle == "Security Settings")
       {
           //we are already here, assign the variable
           $extraVariables = [];
           $extraVariables['ctest'] = 'Please establish a security question for your account<br />This will be used to validate your account should we ever need to<br />';
           return $extraVariables;
       }
       header('Location: clientarea.php?action=security');
   }
   else
   {
       return;
   }
}

add_hook('ClientAreaPage', 1, "check_client_security");

 

Not going to add one for the notification, because users should be notified when passwords are changed anyways , however you bring up a good point about redirecting admin user... That should be bypassed now :)

Share this post


Link to post
Share on other sites

Thanks for the effort, but as I have discovered there are other connotations due to the fact that there is only 1 security question on the primary user. So because of this I do not want to FORCE any user to this page and stop them from performing any other action unless they complete this information. I would rather just give them a reminder.

 

e.g.

As is the case for many clients, they have several sub-accounts, with different permissions, so different people can login and submit tickets, or receive emails etc. One of those people logs in, and is forced to complete the security question before he/she can submit a ticket or do anything.

Now someone else calls up a couple of days later, and is asked the security question "what is your mothers maiden name".

He gives his answer, but it is wrong, because it is not his mothers maiden name, it is the mother of the person who set the question/answer, and he doesn't know who did it and of course doesn't know the maiden name of all his staff's mothers.

 

This security question is thus in itself security issue, because it requires sharing personal details with other people in your company so they can contact providers and answer the security question. So since most websites use the same common questions, now anyone in your company who knows this information could use it to contact any service provider, website etc that you use, which is nothing to do with the company, and bypass the security checks if you have used the same security question, which is highly likely.

 

 

I have attempted to get round the overall issue by adding a generic "secret pass phrase" question that is not personal, and which can be shared. But this again presents an issue as this is more likely to be forgotten due to the fact it is not personal.

Share this post


Link to post
Share on other sites
Unfortunately, no way to display the message without editing that file, but it's a very simple edit
I am not really a fan of editing templates, as I have to remember to redo it every time I update whmcs.

would it possible instead to just popup an alert dialog when the user logs in telling them to set their security question, rather than automatically redirect them. Presumably this could be done with just a hook ?

If you want to customize display, you've gotta edit templates. No way around that.

that isn't strictly true - I feel a quick Tuesday demonstration approaching! :)

 

I have found a problem with this.

I often need to "login as client", but this hook stops me being able to do anything as I get redirected to the security page.

would be possible to just inject a JavaScript alert instead, telling them to set security question, rather than redirect to the security page.

yes. :idea:

 

Thanks for the effort, but as I have discovered there are other connotations due to the fact that there is only 1 security question on the primary user. So because of this I do not want to FORCE any user to this page and stop them from performing any other action unless they complete this information. I would rather just give them a reminder.

i've written a sample hook (technically two hooks in the same file)...

 

<?php

//Show jquery modal popup to clients encouraging them to setup a security question.
//provided by brian!

function client_custom_headoutput_hook($vars) {

   $head_return = '';
   $head_return = '<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
 <script src="'.$vars['BASE_PATH_JS'].'/jquery-ui.js"></script>
 <script>
 $( function() {
   $( "#dialog-confirm" ).dialog({
     resizable: false,
     height: "auto",
     width: 400,
     modal: true,
     buttons: {
       "'.Lang::trans('clientareanavsecurityquestions').'": function() {
       window.location.href = "clientarea.php?action=security";
       },
       "'.Lang::trans('cancel').'": function() {
         $( this ).dialog( "close" );
       }
     }
   });
 } );
 </script>';
   return $head_return;
}
add_hook("ClientAreaHeadOutput",1,"client_custom_headoutput_hook");

function client_custom_headeroutput_hook($vars) {

   $client = Menu::context('client');
   $adminloggedin = $vars['adminMasqueradingAsClient'];
   $subaccount = $vars['loggedinuser']['contactid'];
   $templatefile = $vars['templatefile'];

   if (empty($client->id) or $adminloggedin or $subaccount) { return; }

   if ($client->securityQuestionId == "0" and $templatefile == "clientareahome") {

       $header_return = '';
       $header_return = '<div id="dialog-confirm" title="'.Lang::trans('clientareasecurityquestion').'"><p><span class="ui-icon ui-icon-unlocked" style="float:left; margin:0 7px 70px 0;"></span>'.Lang::trans('clientAreaSecurityNoSecurityQuestions').'</p></div>';
       return $header_return;
   }
}
add_hook("ClientAreaHeaderOutput",1,"client_custom_headeroutput_hook");

so what it does is to add a jquery modal popup to clientareahome if the client doesn't have a security question set - i've tweaked the checks so that if it's an admin masquerading as the client or a subaccount (contact), then neither will see the popup... therefore, only the main client should see it, but you could tweak it more if you needed to be very specific about who sees it... also removed the database query as variables are now being pulled from the template and the Class documentation.

 

WPx3Gzw.png

 

i've used language strings in the hook to make it multilingual - the "Change Security Question" button takes the user to the security questions page, "Cancel" or "X" will simply close the popup... if they dismiss it, then they won't see it on any of the other pages, but if they return to clientareahome, they'll see the popup reminder again... I did toy with making it appear only once per session, but assumed the whole point to this was to encourage them to set the question.

 

also, I can see no reason why you couldn't use this with other modal solutions... e.g if you use Bootstrap instead of jQuery dialog, it's slightly simpler, doesn't necessarily need any additional .js/css files and the output is more flexible... :idea:

 

<?php

//Show Bootstrap modal popup to clients encouraging them to setup a security question.
//provided by brian!

function client_custom_headoutput_hook($vars) {

   $head_return = '';
   $head_return = '<script>
$(window).load(function()
{
   $(\'#myModal\').modal(\'show\');
});
 </script>';
   return $head_return;
}
add_hook("ClientAreaHeadOutput",1,"client_custom_headoutput_hook");

function client_custom_headeroutput_hook($vars) {

   $client = Menu::context('client');
   $adminloggedin = $vars['adminMasqueradingAsClient'];
   $subaccount = $vars['loggedinuser']['contactid'];
   $templatefile = $vars['templatefile'];

   if (empty($client->id) or $adminloggedin or $subaccount) { return; }

   if ($client->securityQuestionId == "0" and $templatefile == "clientareahome") {

       $header_return = '';
       $header_return = '<div class="container">
   <div class="modal fade" id="myModal" role="dialog">
       <div class="modal-dialog">
           <div class="modal-content">
           <div class="modal-header">
               <button type="button" class="close" data-dismiss="modal">×</button>
               <h4 class="modal-title"><i class="fa fa-unlock fa-lg"></i>  '.Lang::trans('clientareasecurityquestion').'</h4>
           </div>
           <div class="modal-body">
               <p>'.Lang::trans('clientAreaSecurityNoSecurityQuestions').'</p>
           </div>
           <div class="modal-footer">
               <a class="btn btn-primary" href="clientarea.php?action=security">'.Lang::trans('clientareanavsecurityquestions').'</a>
               <button type="button" class="btn btn-default" data-dismiss="modal">'.Lang::trans('cancel').'</button>
           </div>
           </div>
       </div>
   </div>
</div>';
       return $header_return;
   }
}
add_hook("ClientAreaHeaderOutput",1,"client_custom_headeroutput_hook");

 

 

0xJiiDB.png

 

oh did I forget to mention - not one single template edit in sight... maybe one day i'll be a WHMCS Guru too! :-P

Share this post


Link to post
Share on other sites
Posted (edited)

Hey @brian!

Hope you are doing well.

1) Does this modal solution still work in current version?  It looks good!!

2) Anyway to actually show the user their current security question answer?  Even if it is partly obfuscated?  At present (at least in my installation) the client side page that shows the Change Security Question - just shows an empty field for the security answer.  I had a client ask why they couldn't see the answer they'd written when they were logged in.

Is that normal behaviour of this screen / page?  If so, how to show part of the answer (or if that's too hard, the entire answer?)

 Portal Home > Client Area > My Details > Security Settings

EDIT: I found this, which looks like it would do the trick, but would be better if the answer could perhaps show in a modal - like the one @brian! has produced for the pop up message.

 

 

Edited by sol2010

Share this post


Link to post
Share on other sites

Hi,

13 hours ago, sol2010 said:

Hope you are doing well.

I am thanks... end of tax year... crazy times!

plus i've got a number of ongoing projects that quite happily worked on v7.4.2, that bizarrely break on v7.5... of course, there is little v7.5 documentation yet to work with (that never changes), so fumbling in the dark and hoping the forthcoming v7.5.1 maintenance update fixes everything!

13 hours ago, sol2010 said:

1) Does this modal solution still work in current version? 

yes it still works in v7.5...

rbxTg7w.png

13 hours ago, sol2010 said:

It looks good!!

yeah someone took the code, made it into a commercial addon and didn't credit me with creating the code for the core idea... welcome to the community!

13 hours ago, sol2010 said:

2) Anyway to actually show the user their current security question answer?  Even if it is partly obfuscated?  At present (at least in my installation) the client side page that shows the Change Security Question - just shows an empty field for the security answer.  I had a client ask why they couldn't see the answer they'd written when they were logged in.

Is that normal behaviour of this screen / page?  If so, how to show part of the answer (or if that's too hard, the entire answer?)

it's normal behaviour in the sense that it's what the template is designed to do - if you wanted to change it as you describe and show the full answer, it would be just require a simple tweak to the clientareasecurity.tpl template by changing...

<input type="password" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" />

to...

<input type="text" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" value="{$clientsdetails.securityqans}" readonly />

4xmQJKb.png

you could display it in other ways, not necessarily a readonly text field - it's just a Smarty variable that you can output anywhere you like!

13 hours ago, sol2010 said:

EDIT: I found this, which looks like it would do the trick, but would be better if the answer could perhaps show in a modal - like the one @brian! has produced for the pop up message.

you could use a hook to query the db for both the question and the answer, but the answer value is available as a Smarty variable once the client is logged in... so is the question ID value, though you'd still need to query the database to convert that id into the relevant question - on any page other than the security page (which already has the question as a variable).

so if you were going to show it as a popup, that's how I would do it... on the security page, a popup hook could easily use the Q&A Smarty variables... anywhere else and you could get the answer from Smarty and the question from the database. :idea:

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.

×

Important Information

By using this site, you agree to our Terms of Use & Guidelines