Handle errors when registration fails

Previously we would raise a 500 error in a variety of cases:

- If a second key was being registered simultaneously (e.g. in a
separate tab), which means the registration state could be missing
after the first registration completes. That smells like an attack.

- If the server-side verification failed e.g. origin verification,
challenge verification, etc. The library seems to use 'ValueError'
for all such errors [1] (after auditing its 'raise' statements, and
excluding AttestationError [2], since we're not doing that).

- If a key is used that attempts to sign with an unsupported
algorithm. This would normally raise a NotImplemented error as part
of verifying attestation [3], but we don't do that, so we need to
verify the algorithm is supported by the library manually.

This adds error handling to return a 400 response and error message
in these cases, since the error is not unexpected (i.e. not a 500).
A 400 seems more appropriate than a 403, since in many cases it's
not clear if the request data is valid.

I've used CBOR for the transport encoding, to match the successful
request / response encoding. Note that the ordering of then/catch
matters in JS - we don't want to catch our own throws!

[1]: https://github.com/Yubico/python-fido2/blob/142587b3e698ca0e253c78d75758fda635cac51a/fido2/server.py#L255
[2]: https://github.com/Yubico/python-fido2/blob/c42d9628a4f33d20c4401096fa8d3fc466d5b77f/fido2/attestation/base.py#L39
[3]: https://github.com/Yubico/python-fido2/blob/c42d9628a4f33d20c4401096fa8d3fc466d5b77f/fido2/cose.py#L92
This commit is contained in:
Ben Thorner
2021-05-17 12:18:24 +01:00
parent bcf2f7dccd
commit 8502827afb
6 changed files with 117 additions and 20 deletions
+12 -6
View File
@@ -3,7 +3,7 @@ from flask import current_app, request, session
from flask_login import current_user
from app.main import main
from app.models.webauthn_credential import WebAuthnCredential
from app.models.webauthn_credential import RegistrationError, WebAuthnCredential
from app.notify_client.user_api_client import user_api_client
from app.utils import user_is_platform_admin
@@ -34,13 +34,19 @@ def webauthn_begin_register():
@main.route('/webauthn/register', methods=['POST'])
@user_is_platform_admin
def webauthn_complete_register():
credential = WebAuthnCredential.from_registration(
session.pop("webauthn_registration_state"),
cbor.decode(request.get_data()),
)
if 'webauthn_registration_state' not in session:
return cbor.encode("No registration in progress"), 400
try:
credential = WebAuthnCredential.from_registration(
session.pop("webauthn_registration_state"),
cbor.decode(request.get_data()),
)
except RegistrationError as e:
return cbor.encode(str(e)), 400
user_api_client.create_webauthn_credential_for_user(
current_user.id, credential
)
return ''
return cbor.encode('')