비밀번호는 계정 보안에서 가장 취약한 부분입니다. 피싱 당하거나, 유출되거나, 재사용되거나, 잊어버릴 수 있습니다. 그래서 우리는 WebAuthn Passkeys 지원을 추가했습니다 - 피싱 방지가 되고 더 안전하며 편리한 비밀번호 없는 인증 방법입니다.

Passkeys란 무엇인가요?

Passkeys는 기기(휴대폰, 노트북, 보안 키)에 저장되어 인증 시 비밀번호를 대체하는 암호화된 자격 증명입니다. WebAuthn 표준(FIDO2의 일부)을 사용하며 여러 가지 장점을 제공합니다:

  • 피싱 방지 - Passkeys는 웹사이트 도메인에 바인딩되어 있어 가짜 사이트에서 사용할 수 없습니다
  • 유출될 비밀이 없음 - 공개 키만 서버에 저장됩니다
  • 기기에 바인딩 - 개인 키는 절대 기기를 떠나지 않습니다
  • 생체 인증으로 보호 - 일반적으로 Face ID, Touch ID 또는 Windows Hello로 보호됩니다
  • 기기 간 동기화 - 최신 플랫폼은 iCloud, Google 또는 Microsoft 계정을 통해 기기 간에 Passkeys를 동기화합니다

우리의 구현

기존 django-allauth 인증 스택에 WebAuthn Passkeys 지원을 통합하여 웹과 모바일 API 엔드포인트를 모두 제공합니다.

등록 흐름

사용자가 새 Passkey를 등록할 때 흐름은 다음과 같이 작동합니다:

  1. 클라이언트가 서버에 등록 옵션 요청
  2. 서버가 사용자 및 신뢰 당사자 정보와 함께 챌린지 생성
  3. 브라우저의 WebAuthn API가 사용자에게 프롬프트 표시(생체 인증 또는 보안 키)
  4. 클라이언트가 자격 증명을 서버로 전송
  5. 서버가 검증하고 공개 키를 저장
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# API endpoint to begin registration
class WebAuthnRegistrationOptionsView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        from allauth.mfa.webauthn.internal import auth as webauthn_auth

        # Generate credential creation options
        creation_options = webauthn_auth.begin_registration(
            request.user,
            passwordless=False  # Use as 2FA, not passwordless
        )

        return Response({
            "success": True,
            "creation_options": creation_options,
        })

인증 흐름

Passkey를 사용한 이중 인증의 경우:

  1. 사용자가 기본 인증 완료 (사용자 이름/비밀번호 또는 소셜 로그인)
  2. 서버가 WebAuthn 검증을 위한 챌린지 반환
  3. 브라우저가 Passkey 요청 (생체 인증 터치)
  4. 저장된 공개 키에 대해 자격 증명 검증
  5. 사용자가 완전히 인증됨
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class WebAuthnVerifyView(APIView):
    def post(self, request):
        credential = request.data.get("credential")

        # Complete authentication and verify credential
        authenticator = webauthn_auth.complete_authentication(
            request.user,
            credential
        )

        # Record usage for security monitoring
        authenticator.last_used_at = timezone.now()
        authenticator.save()

        return Response({"success": True})

Passkey 관리

사용자는 등록된 Passkeys를 관리할 수 있습니다:

  • 모든 Passkeys 나열 - 등록 날짜 및 마지막 사용 시간 포함
  • Passkeys 이름 변경 - 쉬운 식별을 위해 (예: “iPhone”, “MacBook”)
  • Passkeys 삭제 - 마지막 인증기 제거 방지 보호 포함
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class WebAuthnListView(APIView):
    def get(self, request):
        authenticators = Authenticator.objects.filter(
            user=request.user,
            type=Authenticator.Type.WEBAUTHN
        ).order_by("-created_at")

        return Response({
            "passkeys": [
                {
                    "id": auth.pk,
                    "name": auth.wrap().name,
                    "created_at": auth.created_at.isoformat(),
                    "last_used_at": auth.last_used_at.isoformat() if auth.last_used_at else None,
                }
                for auth in authenticators
            ]
        })

API 엔드포인트

우리의 REST API는 모바일 앱에 완전한 WebAuthn 지원을 제공합니다:

엔드포인트메소드설명
/api/webauthn/register/options/GET등록 챌린지 가져오기
/api/webauthn/register/complete/POST등록 완료
/api/webauthn/authenticate/options/GET인증 챌린지 가져오기
/api/webauthn/verify/POST2FA용 자격 증명 검증
/api/webauthn/GET등록된 Passkeys 나열
/api/webauthn/<id>/PATCHPasskey 이름 변경
/api/webauthn/<id>/DELETEPasskey 삭제

보안 고려 사항

복구 코드

사용자가 첫 번째 인증기(Passkey 또는 TOTP)를 등록하면 자동으로 복구 코드를 생성합니다. 이러한 일회용 코드는 다른 모든 인증 방법을 사용할 수 없게 되었을 때 사용할 수 있습니다:

1
2
if Authenticator.objects.filter(user=request.user).count() == 1:
    auto_generate_recovery_codes(request._request)

잠금 방지

사용자는 이중 인증 방법이 없어지는 경우 마지막 Passkey를 삭제할 수 없습니다:

1
2
3
4
if not adapter.can_delete_authenticator(authenticator):
    return Response({
        "message": "Cannot delete - you must have at least one authentication method"
    }, status=400)

모니터링

보안 모니터링을 위해 Datadog 메트릭을 통해 Passkey 사용량을 추적합니다:

1
2
3
increment("webauthn.registration.success")
increment("webauthn.verify.success")
increment("webauthn.verify.failed")

브라우저 지원

WebAuthn은 모든 최신 브라우저에서 지원됩니다:

  • Chrome 67+
  • Firefox 60+
  • Safari 13+
  • Edge 79+
  • 모바일 Chrome, Safari, Firefox

오래된 브라우저의 경우 사용자는 TOTP(인증 앱) 또는 복구 코드를 계속 사용할 수 있습니다.

사용자 경험

Passkey 추가는 몇 초면 됩니다:

  1. 설정 → 보안 → 이중 인증으로 이동
  2. Passkey 추가 클릭
  3. 기기 이름 지정 (예: “iPhone 15”)
  4. Face ID / Touch ID / Windows Hello로 인증
  5. 완료! Passkey가 등록되었습니다

향후 로그인은 생체 인증 확인만 필요합니다 - 비밀번호를 입력하거나 인증 앱에서 코드를 복사할 필요가 없습니다.

Passkeys를 선택한 이유

방법피싱 위험사용자 경험복구
비밀번호높음나쁨 (잊어버림, 약함)이메일 재설정
SMS 2FA중간 (SIM 스왑)보통전화번호
TOTP 앱낮음수동 코드 입력백업 키
Passkeys매우 낮음우수복구 코드

Passkeys는 인증의 미래를 대표합니다. 이미 Apple, Google, Microsoft 플랫폼에서 지원되어 거의 모든 사용자가 접근할 수 있습니다.


비밀번호 없는 인증을 시작할 준비가 되셨나요? 보안 설정에서 Passkeys를 활성화하세요!