davidearl / webauthn

An implementation of webauthn in PHP on the server side (e.g Yubico 2 and Google Titan keys)
https://webauthn.davidearl.uk
MIT License
132 stars 24 forks source link

Dissallow reusing login payloads #40

Closed andrewscode closed 1 year ago

andrewscode commented 4 years ago

I've noticed that the signed challenges can be reused. From your example there's nothing that validates the challenge that was sent to the browser, was the same challenge that was sent back. For example:

Challenge sent bytes: 1,2,3,4,5,6,etc Challenge is signed and the originalChallenge is sent back to 1,2,3,4,5,6,etc

Another challenge is sent with bytes: 7,8,9,0,1,2,3,etc Previous challenge is sent back with 1,2,3,4,5,6,etc. and accepted

I think the challenge that is sent to the browser needs to be stored in the SESSION and compared with when receving the signed challenge.

I solved this in my code:

        $login = json_decode($_POST["login"]);
        $original = json_decode($_SESSION["original"]);

        if($original->challenge !== $login->originalChallenge) {
            return false;
        }

        if(!$webauthn->authenticate($_POST["login"], $userwebauthn)) {
            return false;
        }
                $_SESSION["original"] = null;

However this should probably be one of the parameters to authenticate.

Skittel commented 4 years ago

Hello,

I can confirm this.

I changed the code to use as few javascript as possible and no AJAX to reduce failure points. I have a PHP-file which creates the challenge and outputs the html with the JS. The JS communicates with the OS and calls itself with the result verifying the challenge and shows a result html. The way a standard html/php login would work.

The main-problem, as far as I can see it, is that the challenge and the response are not linked together in any way. I can only verify that the response is a correct answer by the yubikey. So the client could send a 10 year old response and it would look fine.

An easy solution would be to put a random value (or date, ip, agentstring, ...) as id into the challenge. This way you can verify if the response is linked to your challenge.

Stefan