lbuchs / WebAuthn

A simple PHP WebAuthn (FIDO2/Passkey) server library
https://webauthn.lubu.ch
MIT License
419 stars 75 forks source link

U2F Migration and PHP8 #56

Open DigitalCyberSoft opened 2 years ago

DigitalCyberSoft commented 2 years ago

I'm not exactly saying this is a bug, but it might help some others out with migration.

First up the public certificate needs to be adjusted, and the rpId need to be matched to the kind of key use.

                                        $webAuthn = new lbuchs\WebAuthn\WebAuthn('App Name', $key['appid'], $formats);
                                        if (strpos($key['appid'], 'https://') !== false)
                                        {
                                                $der  = "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01";
                                                $der .= "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42";
                                                $der .= "\0".base64_decode($key['publicKey']);

                                                $pem  = "-----BEGIN PUBLIC KEY-----\r\n";
                                                $pem .= chunk_split(base64_encode($der), 64);
                                                $pem .= "-----END PUBLIC KEY-----";
                                                $credentialPublicKey = $pem;
                                        }
                                        else
                                        {
                                                $credentialPublicKey = $key['publicKey'];
                                        }

Second the u2f keyHandle needs to be fixed, and all known keys need to be put into the credentials.get (template code is Smarty, adjust as needed)

U2F stored the keyHandle in an url safe base64 format that causes issues. Easily fixed

$keyHandle = base64_decode(str_pad(strtr($reg['keyHandle'], '-_', '+/'), strlen($reg['keyHandle']) % 4, '=', STR_PAD_RIGHT));
$reg['keyHandle'] = base64_encode($keyHandle);
                                        {if $u2fKeys || $webAuthnKeys}
                                        allowCredentials: [
                                                {foreach $u2fKeys as $key}
                                                {
                                                        type: 'public-key',
                                                        id: _base64ToArrayBuffer('{$key.keyHandle}')
                                                },
                                                {/foreach}
                                                {foreach $webAuthnKeys as $key}
                                                {
                                                        type: 'public-key',
                                                        id: _base64ToArrayBuffer('{$key.keyHandle}')
                                                },
                                                {/foreach}
                                        ],
                                        {/if}

PHP8 seems to have a problem with the getChallenge() method. Easy enough to store it as:

$_SESSION['webAuthnChallenge'] = $challenge->publicKey->challenge->getBinaryString();

Lastly the checkOrigin() from WebAuthn will fail due to the regex when passing in https://domain.com

Realistically there should be a better check for this or a flag, but a quick fix is simply

return \preg_match('/' . \preg_quote(\str_replace('https://', '', $this->_rpId)) . '$/i', $host) === 1;

I also found Chrome doesn't follow the standards, and a lot of defaults are ignored and are instead required.

lbuchs commented 1 year ago

Lastly the checkOrigin() from WebAuthn will fail due to the regex when passing in https://domain.com/

this is not a bug, when you pass rpId at the constructor you have to add the domain name and not a URL, so the domain name of your example is just domain.com.