Make WebAuthn clone detection actually block, and fix false positives
Two problems with sign-count clone detection: - suspicious_sign_count? flagged the case where both the stored and presented counts are 0. Most synced passkeys (Apple/Google) report 0 every time, so every legitimate sign-in was flagged — drowning real signals in noise. Per WebAuthn §6.1.1 a 0 counter means "no counter"; only flag when BOTH counts are non-zero and the new one does not advance. - On a suspicious count the controller only logged a warning and then continued to authenticate and overwrite the stored counter. A cloned credential therefore worked indefinitely. webauthn_verify now rejects the sign-in (no session, no counter update) and emails the user via a new SecurityMailer#suspicious_passkey_used. Tests cover the corrected classification (synced/first-use/normal vs equal/ decreasing) and the new alert email. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -295,10 +295,14 @@ class SessionsController < ApplicationController
|
||||
sign_count: stored_credential.sign_count
|
||||
)
|
||||
|
||||
# Check for suspicious sign count (possible clone)
|
||||
# Clone detection: a non-advancing signature counter signals the credential
|
||||
# may have been copied. Reject the sign-in (do NOT create a session or update
|
||||
# the stored counter) and alert the user, per WebAuthn §6.1.1.
|
||||
if stored_credential.suspicious_sign_count?(webauthn_credential.sign_count)
|
||||
Rails.logger.warn "Suspicious WebAuthn sign count for user #{user.id}, credential #{stored_credential.id}"
|
||||
# You might want to notify admins or temporarily disable the credential
|
||||
Rails.logger.warn "Suspicious WebAuthn sign count for user #{user.id}, credential #{stored_credential.id} (stored=#{stored_credential.sign_count}, presented=#{webauthn_credential.sign_count})"
|
||||
SecurityMailer.suspicious_passkey_used(user, nickname: stored_credential.display_name, **security_event_context).deliver_later
|
||||
render json: {error: "Passkey authentication could not be completed. Please contact support."}, status: :unprocessable_entity
|
||||
return
|
||||
end
|
||||
|
||||
# Update credential usage
|
||||
|
||||
Reference in New Issue
Block a user