[DigitalPoint] Security & Passkeys

[DigitalPoint] Security & Passkeys 1.1.8

No permission to download
Hmmm... I wasn't able to duplicate it on this end. From the error you posted, it doesn't appear like it should be anything related to the version of PHP you are using. The only thing I can think of is if somehow the xf_user_tfa security key record for the user might have been altered by something somehow. If you are able to query the database direct and you want me to have a look, run this query and send me the results (privately of course):
SQL:
SELECT provider_data FROM xf_user_tfa WHERE provider_id = 'security_key' AND user_id = {user_id_having_issue};

There's nothing in there that is a security issue, but feel free to delete the IP addresses in there if you want. The public key is just that... keys intended for the public. Can only use it to encrypt something that only the private key can decrypt (private key is in your hardware device).

Another option if you don't want to send it to me is just delete that record xf_user_tfa and try adding/deleting again to see if you can replicate it again. But definitely sounds like there's some sort of issue/corruption/missing data in xf_user_tfa somehow.
 
What could be the problem: I guess the entered pincode for the Yubikey was wrong multiple times. That could throw things off.

Will try your fixes asap and will let you know.
 
The pin code shouldn't matter (in theory anyway) because it would never be a successful authentication without it if you are requiring a pin. It should only do something (like save the credentials) if it's valid (however that may be).
 
I removed the passkey in the DB directly and registered it again.

Now it works correctly (I also reset the pincode on the Yubikey I had lying around). Thanks for the help @digitalpoint

PS. Something I noticed, the logged IP is that from my local testserver, not from my client (Macbook). Is that by design?
 
it logs the client IP (not server). But if your server is setup as a proxy and not passing through client IP or something you could see that (would apply to any IP logging, not just this addon).
 
Not directly related to addon, but Apple IDs just got support for hardware security keys today. Can use YubiKeys and other compatible hardware keys to secure Apple ID accounts now if you have the latest iOS or macOS. Hopefully it helps "normal people" secure their stuff and get some publicity for how good hardware keys are for securing accounts.

1674536541030.png
 
  • Like
Reactions: ENF
A couple of bugs I just found in ACP:
  • ErrorException: [E_NOTICE] Undefined index: clientDataJSON
  • src/addons/DigitalPoint/Security/Tfa/SecurityKey.php:74
Code:
#0 src/addons/DigitalPoint/Security/Tfa/SecurityKey.php(74): XF::handlePhpError(8, '[E_NOTICE] Unde...', '/home/*****/...', 74, Array)
#1 src/addons/DigitalPoint/Security/XF/Pub/Controller/Account.php(132): DigitalPoint\Security\Tfa\SecurityKey->verify('setup', Object(SV\DailyStatistics\XF\Entity\User), Array, Object(XF\Http\Request))
#2 src/XF/Mvc/Dispatcher.php(352): DigitalPoint\Security\XF\Pub\Controller\Account->actionTwoStepAdd(Object(XF\Mvc\ParameterBag))
#3 src/XF/Mvc/Dispatcher.php(259): XF\Mvc\Dispatcher->dispatchClass('XF:Account', 'TwoStepadd', Object(XF\Mvc\RouteMatch), Object(SV\SearchImprovements\XF\Pub\Controller\Account), NULL)
#4 src/XF/Mvc/Dispatcher.php(115): XF\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Mvc\RouteMatch), Object(SV\SearchImprovements\XF\Pub\Controller\Account), NULL)
#5 src/XF/Mvc/Dispatcher.php(57): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Mvc\RouteMatch))
#6 src/XF/App.php(2483): XF\Mvc\Dispatcher->run()
#7 src/XF.php(524): XF\App->run()
#8 index.php(20): XF::runApp('XF\\Pub\\App')
#9 {main}

Code:
array(4) {
  ["url"] => string(34) "/account/two-step/security_key/add"
  ["referrer"] => string(62) "/account/two-step/security_key/add"
  ["_GET"] => array(0) {
  }
  ["_POST"] => array(7) {
    ["_xfToken"] => string(8) "********"
    ["name"] => string(6) "name_username"
    ["payload"] => string(0) ""
    ["step"] => string(7) "confirm"
    ["_xfRequestUri"] => string(34) "/account/two-step/security_key/add"
    ["_xfWithData"] => string(1) "1"
    ["_xfResponseType"] => string(4) "json"
  }
}


and

  • ErrorException: [E_NOTICE] Undefined index: attestationObject
  • src/addons/DigitalPoint/Security/Tfa/SecurityKey.php:86
Code:
#0 src/addons/DigitalPoint/Security/Tfa/SecurityKey.php(86): XF::handlePhpError(8, '[E_NOTICE] Unde...', '/home/*****/...', 86, Array)
#1 src/addons/DigitalPoint/Security/XF/Pub/Controller/Account.php(132): DigitalPoint\Security\Tfa\SecurityKey->verify('setup', Object(SV\DailyStatistics\XF\Entity\User), Array, Object(XF\Http\Request))
#2 src/XF/Mvc/Dispatcher.php(352): DigitalPoint\Security\XF\Pub\Controller\Account->actionTwoStepAdd(Object(XF\Mvc\ParameterBag))
#3 src/XF/Mvc/Dispatcher.php(259): XF\Mvc\Dispatcher->dispatchClass('XF:Account', 'TwoStepadd', Object(XF\Mvc\RouteMatch), Object(SV\SearchImprovements\XF\Pub\Controller\Account), NULL)
#4 src/XF/Mvc/Dispatcher.php(115): XF\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Mvc\RouteMatch), Object(SV\SearchImprovements\XF\Pub\Controller\Account), NULL)
#5 src/XF/Mvc/Dispatcher.php(57): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Mvc\RouteMatch))
#6 src/XF/App.php(2483): XF\Mvc\Dispatcher->run()
#7 src/XF.php(524): XF\App->run()
#8 index.php(20): XF::runApp('XF\\Pub\\App')
#9 {main}

Code:
array(4) {
  ["url"] => string(34) "/account/two-step/security_key/add"
  ["referrer"] => string(62) "/account/two-step/security_key/add"
  ["_GET"] => array(0) {
  }
  ["_POST"] => array(7) {
    ["_xfToken"] => string(8) "********"
    ["name"] => string(6) "name_username"
    ["payload"] => string(0) ""
    ["step"] => string(7) "confirm"
    ["_xfRequestUri"] => string(34) "/account/two-step/security_key/add"
    ["_xfWithData"] => string(1) "1"
    ["_xfResponseType"] => string(4) "json"
  }
}
 
Is this something you are able to replicate yourself? From the looks of it, it appears to possibly be a browser issue where the browser isn't sending an empty payload or payload is being stripped somewhere along the way of the network path. Do you use Cloudflare by chance? If so, what's the security level set for your site in your Cloudflare account? Their security level function is notorious for dropping parts of HTTP requests if they think it could be malicious... and "payload" sounds like it could possibly be malicious. If it's not something you are able to replicate, you might have to chalk it to to a browser issue if the user was using an old/out of date browser that maybe doesn't fully support WebAuthn/FIDO2 (the ability to use security keys is dependent on the browser's ability to do so).

It shows payload was submitted, but empty (which in theory should never be the case).
 
Actually, scratch that... I was able to replicate it. If someone starts the process, names the key, but doesn't actually authorize a key and then submits the name without the authorization (the submit button is disabled at that point, but you can hit return in the key name field to submit the form anyway). In that case, they would have gotten a generic error and you would see what you saw in the error log. Probably makes sense to not log it to the error log and give the user a better error (you need a key dummy! haha)
 
According to what little I could see in the ACP, this user activated the two-step verification and that error appeared.

I don't use Cloudflare
 
How difficult would it be to make sending OTP via text? Are there server requirements for it?
 
How difficult would it be to make sending OTP via text? Are there server requirements for it?
It would be impossible with this addon because Passkeys and security keys aren’t one time passwords. It’s public/private key encryption. The hardware is asked to decrypt something that only it can decrypt (the private key used for decryption is internal to the device). Can’t do that over text.
 
Are there any plans to make what's shown to each usergroup selectable? IMO, most users are overwhelmed by this type of data, not realizing how much info they provide to a website with every login/click they make. IP and Session data in particular, I'd prefer to not present that info to the members.

I can see the data being useful from an Admin standpoint however, especially when looking at accounts that may be spam/scam related.
 
Last edited:
On a local XF with the last release:

Code:
ErrorException: [E_DEPRECATED] substr(): Passing null to parameter #1 ($string) of type string is deprecated src\addons\DigitalPoint\Security\Entity\UserExtra.php:40
Generated by: Nicolas Feb 8, 2023 at 2:35 PM
Code:
Stack trace
#0 [internal function]: XF::handlePhpError(8192, '[E_DEPRECATED] ...', 'C:\\laragon\\www\\...', 40)
#1 src\addons\DigitalPoint\Security\Entity\UserExtra.php(40): substr(NULL, 0, 2)
#2 src\XF\Mvc\Entity\Entity.php(798): DigitalPoint\Security\Entity\UserExtra->verifyCountry(NULL, 'country', 5, Array)
#3 src\XF\Mvc\Entity\Entity.php(643): XF\Mvc\Entity\Entity->_verifyValueCustom(NULL, 'country', 5, Array)
#4 src\XF\Mvc\Entity\Entity.php(577): XF\Mvc\Entity\Entity->set('country', NULL)
#5 src\addons\DigitalPoint\Security\Repository\UserExtra.php(19): XF\Mvc\Entity\Entity->__set('country', NULL)
#6 src\addons\DigitalPoint\Security\XF\Entity\UserRemember.php(30): DigitalPoint\Security\Repository\UserExtra->logExtra(42, 'user_remember')
#7 src\addons\DigitalPoint\Security\XF\Entity\UserRemember.php(23): DigitalPoint\Security\XF\Entity\UserRemember->logExtra()
#8 src\XF\Mvc\Entity\Entity.php(1277): DigitalPoint\Security\XF\Entity\UserRemember->_postSave()
#9 src\XF\Repository\UserRemember.php(17): XF\Mvc\Entity\Entity->save()
#10 src\XF\ControllerPlugin\Login.php(282): XF\Repository\UserRemember->createRememberRecord(1)
#11 src\XF\ControllerPlugin\Login.php(219): XF\ControllerPlugin\Login->createVisitorRememberKey()
#12 src\XF\Pub\Controller\Login.php(125): XF\ControllerPlugin\Login->completeLogin(Object(XenAddons\LD\XF\Entity\User), true)
#13 src\XF\Mvc\Dispatcher.php(352): XF\Pub\Controller\Login->actionLogin(Object(XF\Mvc\ParameterBag))
#14 src\XF\Mvc\Dispatcher.php(259): XF\Mvc\Dispatcher->dispatchClass('XF:Login', 'Login', Object(XF\Mvc\RouteMatch), Object(XF\Pub\Controller\Login), NULL)
#15 src\XF\Mvc\Dispatcher.php(115): XF\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Mvc\RouteMatch), Object(XF\Pub\Controller\Login), NULL)
#16 src\XF\Mvc\Dispatcher.php(57): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Mvc\RouteMatch))
#17 src\XF\App.php(2483): XF\Mvc\Dispatcher->run()
#18 src\XF.php(524): XF\App->run()
#19 index.php(20): XF::runApp('XF\\Pub\\App')
#20 {main}
Code:
array(4) {
  ["url"] => string(27) "/test/index.php?login/login"
  ["referrer"] => string(32) "https://localhost/test/index.php"
  ["_GET"] => array(1) {
    ["login/login"] => string(0) ""
  }
  ["_POST"] => array(5) {
    ["_xfToken"] => string(8) "********"
    ["login"] => string(7) "Nicolas"
    ["password"] => string(8) "********"
    ["remember"] => string(1) "1"
    ["_xfRedirect"] => string(32) "https://localhost/test/index.php"
  }
}
Deactivate then activate the add-on solved the problem.
 
Are there any plans to make what's shown to each usergroup selectable? IMO, most users are overwhelmed by this type of data, not realizing how much info they provide to a website with every login/click they make. I think the IP and Session data is very useful from an Admin standpoint, but I'd prefer to not present that info to the members.

I can see the data being useful from an Admin standpoint however, especially when looking at accounts that may be spam/scam related.
It wouldn't be terribly hard, but going out of your way to take away user's ability to manage their own sessions might not be the best idea. Pretty much every major website allows users to see their session data so they can see if there are any that are unexpected. For example...

Facebook:
1675868224778.webp

Google:
1675868248710.webp

Say a user sees a session pop up on their account in some random place (say India or China), they can mitigate it in some cases (terminate the session and change their account password before real damage is done). While it's useful for admins, I wouldn't exactly call every site that gives users that data (like Facebook) exasctly the most tech savvy users, but they still get it.
 
On a local XF with the last release:

Code:
ErrorException: [E_DEPRECATED] substr(): Passing null to parameter #1 ($string) of type string is deprecated src\addons\DigitalPoint\Security\Entity\UserExtra.php:40
Generated by: Nicolas Feb 8, 2023 at 2:35 PM
Code:
Stack trace
#0 [internal function]: XF::handlePhpError(8192, '[E_DEPRECATED] ...', 'C:\\laragon\\www\\...', 40)
#1 src\addons\DigitalPoint\Security\Entity\UserExtra.php(40): substr(NULL, 0, 2)
#2 src\XF\Mvc\Entity\Entity.php(798): DigitalPoint\Security\Entity\UserExtra->verifyCountry(NULL, 'country', 5, Array)
#3 src\XF\Mvc\Entity\Entity.php(643): XF\Mvc\Entity\Entity->_verifyValueCustom(NULL, 'country', 5, Array)
#4 src\XF\Mvc\Entity\Entity.php(577): XF\Mvc\Entity\Entity->set('country', NULL)
#5 src\addons\DigitalPoint\Security\Repository\UserExtra.php(19): XF\Mvc\Entity\Entity->__set('country', NULL)
#6 src\addons\DigitalPoint\Security\XF\Entity\UserRemember.php(30): DigitalPoint\Security\Repository\UserExtra->logExtra(42, 'user_remember')
#7 src\addons\DigitalPoint\Security\XF\Entity\UserRemember.php(23): DigitalPoint\Security\XF\Entity\UserRemember->logExtra()
#8 src\XF\Mvc\Entity\Entity.php(1277): DigitalPoint\Security\XF\Entity\UserRemember->_postSave()
#9 src\XF\Repository\UserRemember.php(17): XF\Mvc\Entity\Entity->save()
#10 src\XF\ControllerPlugin\Login.php(282): XF\Repository\UserRemember->createRememberRecord(1)
#11 src\XF\ControllerPlugin\Login.php(219): XF\ControllerPlugin\Login->createVisitorRememberKey()
#12 src\XF\Pub\Controller\Login.php(125): XF\ControllerPlugin\Login->completeLogin(Object(XenAddons\LD\XF\Entity\User), true)
#13 src\XF\Mvc\Dispatcher.php(352): XF\Pub\Controller\Login->actionLogin(Object(XF\Mvc\ParameterBag))
#14 src\XF\Mvc\Dispatcher.php(259): XF\Mvc\Dispatcher->dispatchClass('XF:Login', 'Login', Object(XF\Mvc\RouteMatch), Object(XF\Pub\Controller\Login), NULL)
#15 src\XF\Mvc\Dispatcher.php(115): XF\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Mvc\RouteMatch), Object(XF\Pub\Controller\Login), NULL)
#16 src\XF\Mvc\Dispatcher.php(57): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Mvc\RouteMatch))
#17 src\XF\App.php(2483): XF\Mvc\Dispatcher->run()
#18 src\XF.php(524): XF\App->run()
#19 index.php(20): XF::runApp('XF\\Pub\\App')
#20 {main}
Code:
array(4) {
  ["url"] => string(27) "/test/index.php?login/login"
  ["referrer"] => string(32) "https://localhost/test/index.php"
  ["_GET"] => array(1) {
    ["login/login"] => string(0) ""
  }
  ["_POST"] => array(5) {
    ["_xfToken"] => string(8) "********"
    ["login"] => string(7) "Nicolas"
    ["password"] => string(8) "********"
    ["remember"] => string(1) "1"
    ["_xfRedirect"] => string(32) "https://localhost/test/index.php"
  }
}
Deactivate then activate the add-on solved the problem.
If you edit the DigitalPoint/Security/Entity/UserExtra.php file, change this:
Code:
'default' => null

to this:
Code:
'default' => ''

...and it should fix it. It's basically an issue where no country was detected (like if you are on localhost).

Fixed for the next release.
 
It wouldn't be terribly hard, but going out of your way to take away user's ability to manage their own sessions might not be the best idea. Pretty much every major website allows users to see their session data so they can see if there are any that are unexpected. For example...
That poses another question....I know IP data is easily accessible with an Admin account, is this other data accessible as well in the ACP?
 
Top Bottom