Skip to content

Commit

Permalink
switch from default deny to default allow for security keys
Browse files Browse the repository at this point in the history
a security key no longer needs to be in metadata to be valid for mfa
  • Loading branch information
johanlundberg committed Dec 17, 2024
1 parent fb8569b commit bb5f6bc
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 54 deletions.
20 changes: 9 additions & 11 deletions src/eduid/webapp/security/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ class SecurityConfig(

# webauthn
webauthn_proofing_method: str = Field(default="webauthn metadata")
webauthn_proofing_version: str = Field(default="2022v1")
webauthn_proofing_version: str = Field(default="2024v1")
webauthn_max_allowed_tokens: int = 10
webauthn_attestation: AttestationConveyancePreference | None = None
webauthn_user_verification: UserVerificationRequirement = UserVerificationRequirement.PREFERRED
webauthn_allowed_user_verification_methods: list[str] = Field(
webauthn_recommended_user_verification_methods: list[str] = Field(
default=[
"faceprint_internal",
"passcode_external",
Expand All @@ -59,18 +59,16 @@ class SecurityConfig(
"apple",
]
)
webauthn_allowed_key_protection: list[str] = Field(
webauthn_recommended_key_protection: list[str] = Field(
default=["remote_handle", "hardware", "secure_element", "tee", "apple"]
)
webauthn_allowed_status: list[AuthenticatorStatus] = Field(
webauthn_disallowed_status: list[AuthenticatorStatus] = Field(
default=[
AuthenticatorStatus.FIDO_CERTIFIED,
AuthenticatorStatus.FIDO_CERTIFIED_L1,
AuthenticatorStatus.FIDO_CERTIFIED_L2,
AuthenticatorStatus.FIDO_CERTIFIED_L3,
AuthenticatorStatus.FIDO_CERTIFIED_L1plus,
AuthenticatorStatus.FIDO_CERTIFIED_L2plus,
AuthenticatorStatus.FIDO_CERTIFIED_L3plus,
AuthenticatorStatus.USER_VERIFICATION_BYPASS,
AuthenticatorStatus.ATTESTATION_KEY_COMPROMISE,
AuthenticatorStatus.USER_KEY_REMOTE_COMPROMISE,
AuthenticatorStatus.USER_KEY_PHYSICAL_COMPROMISE,
AuthenticatorStatus.REVOKED,
]
)

Expand Down
56 changes: 13 additions & 43 deletions src/eduid/webapp/security/webauthn_proofing.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,63 +134,33 @@ def is_authenticator_mfa_approved(authenticator_info: AuthenticatorInformation)
"""
This is our current policy for determine if a FIDO2 authenticator can do multi-factor authentications.
"""
# If there is no attestation we can not trust the authenticator info
if authenticator_info.attestation_format == AttestationFormat.NONE:
return False
if authenticator_info.user_verified:
current_app.logger.info(
f"AttestationFormat {authenticator_info.attestation_format}: user verified - authenticator is mfa capable"
)

# Our current policy is that Apple is capable of mfa
if authenticator_info.status is OtherAuthenticatorStatus.APPLE:
current_app.logger.debug("apple device is mfa capable")
return True
# check status in metadata (if any) and disallow incident statuses
if authenticator_info.status and authenticator_info.status in current_app.conf.webauthn_disallowed_status:
current_app.logger.debug(f"status {authenticator_info.status} is not mfa capable")
return False

# check status in metadata and disallow uncertified and incident statuses
if authenticator_info.status not in current_app.conf.webauthn_allowed_status:
current_app.logger.debug(f"status {authenticator_info.status} is not mfa capable")
return False

# true if the authenticator supports any of the user verification methods we allow
is_accepted_user_verification = any(
[
method
for method in authenticator_info.user_verification_methods
if method in current_app.conf.webauthn_allowed_user_verification_methods
]
)
# a typical token has key protection ["hardware"] or ["hardware", "tee"] but some also support software, so
# we have to check that all key protections supported is in our allow list
is_accepted_key_protection = all(
[
protection
for protection in authenticator_info.key_protection
if protection in current_app.conf.webauthn_allowed_key_protection
]
)
current_app.logger.debug(f"is_accepted_user_verification: {is_accepted_user_verification}")
current_app.logger.debug(f"is_accepted_key_protection: {is_accepted_key_protection}")
if is_accepted_user_verification and is_accepted_key_protection:
return True
current_app.logger.info(
f"AttestationFormat {authenticator_info.attestation_format}: user NOT verified - authenticator is not mfa capable"
)
return False


def save_webauthn_proofing_log(eppn: str, authenticator_info: AuthenticatorInformation) -> bool:
user_verification_methods_match = set(authenticator_info.user_verification_methods) & set(
current_app.conf.webauthn_allowed_user_verification_methods
)
current_app.logger.debug(f"user verifications methods that match config: {user_verification_methods_match}")
key_protection_match = set(authenticator_info.key_protection) & set(
current_app.conf.webauthn_allowed_key_protection
)
current_app.logger.debug(f"user verifications methods that match config: {user_verification_methods_match}")

proofing_element = WebauthnMfaCapabilityProofingLog(
created_by=current_app.conf.app_name,
eppn=eppn,
proofing_version=current_app.conf.webauthn_proofing_version,
proofing_method=current_app.conf.webauthn_proofing_method,
authenticator_id=authenticator_info.authenticator_id,
attestation_format=authenticator_info.attestation_format,
user_verification_methods=list(user_verification_methods_match),
key_protection=list(key_protection_match),
user_verification_methods=authenticator_info.user_verification_methods,
key_protection=authenticator_info.key_protection,
)
current_app.logger.debug(f"webauthn mfa capability proofing element: {proofing_element}")
return current_app.proofing_log.save(proofing_element)

0 comments on commit bb5f6bc

Please sign in to comment.