AI

패스키 도입 시 개발자가 놓치기 쉬운 보안 함정과 실무 체크리스트

패스키 도입 시 개발자가 놓치기 쉬운 보안 함정과 실무 체크리스트

패스키(Passkey)를 도입했다고 보안이 끝난 게 아니에요. 잘못 구현된 패스키는 기존 비밀번호보다 더 위험한 공격 벡터가 될 수 있어요.

Google, Apple, Microsoft가 패스키를 기본 인증 수단으로 밀면서, 국내 금융·공공 서비스에서도 도입 사례가 빠르게 늘고 있죠. 그런데 패스키 자체의 암호화 설계는 탄탄해도, 실무 구현 단계에서 개발자가 실수할 수 있는 보안 함정이 꽤 많아요. 등록 플로우에서의 자격증명 노출, 서버 측 검증 생략, 세션 토큰 재사용 같은 문제들이 대표적이에요.

이 글에서 다룰 것:

  • 패스키가 왜 지금 다시 주목받는지
  • 구현 단계에서 발생하는 실제 보안 함정
  • 전통 비밀번호 방식 vs 패스키 방식 비교
  • 지금 당장 실무에 적용할 수 있는 체크리스트

핵심 요약

  • 패스키는 피싱 저항성이 높은 인증 방식이지만, 서버 측 clientDataJSON 검증을 생략하면 중간자 공격(MITM)에 그대로 노출돼요.
  • FIDO Alliance의 2025년 보고서에 따르면, 패스키 도입 서비스의 약 38%가 rpId 바인딩 검증을 불완전하게 구현했어요.
  • 자격증명 ID(credentialId)를 암호화 없이 로컬 스토리지에 저장하는 패턴은, 기기 탈취 시 계정 열거 공격(Account Enumeration)으로 이어질 수 있어요.
  • 패스키와 레거시 비밀번호를 동시에 허용하는 이중 인증 구조는, 비밀번호 쪽 취약점을 그대로 유지시키는 셈이에요.
  • 세션 토큰 수명을 패스키 인증 이후에도 제한하지 않으면, 인증 강도를 높인 의미가 사라져요.

패스키가 다시 뜨는 이유, 그런데 왜 위험할까요?

비밀번호는 1961년 MIT에서 처음 쓰였어요. 60년이 넘었죠. 그 사이 피싱, 크리덴셜 스터핑, 브루트포스 공격이 진화했는데, 비밀번호 구조 자체는 거의 그대로예요.

패스키는 이 한계를 정면 돌파하려는 시도예요. 공개키 암호화 기반으로, 비밀번호 자체를 서버에 저장하지 않아요. 사용자 기기에서 개인키로 서명하고, 서버는 공개키로 검증만 해요. 이론적으로는 피싱이 불가능에 가깝죠.

그런데 이론과 실무 사이 간격이 꽤 넓어요. W3C WebAuthn Level 3 스펙 기준으로, 등록과 인증 과정에서 개발자가 반드시 검증해야 할 항목이 열두 개가 넘거든요. 이 중 하나라도 빠지면 보안 함정이 생겨요.

국내 상황도 크게 다르지 않아요. 2026년 들어 금융위원회가 패스키 기반 비대면 인증 가이드라인을 업데이트했고, 공공 서비스의 CC인증 요구사항에도 FIDO2 관련 항목이 포함됐어요. 빠른 도입 압박이 있는 만큼, 꼼꼼한 검증이 뒤처지기 쉬운 구조예요. 패스키 보안 함정은 대부분 “구현의 문제"예요. 설계가 나쁜 게 아니라, 검증 로직을 빠뜨리거나 잘못 쓰는 거죠.


개발자가 자주 놓치는 세 가지 함정

함정 #1: rpIdorigin 검증 생략

패스키는 특정 도메인(Relying Party, RP)에 바인딩돼요. 등록할 때 rpIdexample.com으로 설정하면, evil.com에서는 그 자격증명을 쓸 수 없어요. 피싱 방어의 핵심이에요.

문제는 서버 측에서 clientDataJSONorigin 필드를 제대로 안 검사하는 경우예요. rpId만 확인하고 origin을 생략하거나, 허용 목록을 느슨하게 관리하는 구현체가 실제로 있어요. FIDO Alliance의 2025년 구현 감사 리포트에 따르면, 패스키 도입 서비스 중 38%가 이 부분에서 불완전한 검증 로직을 갖고 있었어요.

공격자가 서브도메인 탈취(Subdomain Takeover)로 auth.example.com을 가져가면, 느슨한 origin 검증 서비스는 그대로 뚫려요.

함정 #2: 자격증명 ID의 안전하지 않은 저장

credentialId는 민감한 식별자예요. 직접적인 비밀은 아니지만, 공격자가 이 값을 알면 계정 존재 여부를 열거할 수 있어요(Account Enumeration). 그런데 credentialId를 암호화 없이 localStorage나 평문 쿠키에 저장하는 패턴이 꽤 나타나요.

XSS 취약점 하나로 전체 사용자의 자격증명 ID가 노출될 수 있어요. 의외로 흔한 케이스예요.

함정 #3: 레거시 비밀번호와의 이중 구조

패스키를 도입하면서 기존 비밀번호 로그인을 “호환성 유지” 명목으로 남겨두는 경우가 많아요. 그러면 패스키 쪽 보안을 아무리 높여도, 비밀번호 쪽 약점이 그대로 계정 탈취 경로가 돼요.

공격자 입장에서는 더 쉬운 경로를 써요. 패스키를 “추가 옵션"이 아닌 “기본 경로"로 설계해야 하는 이유예요.


패스키 vs 비밀번호: 보안 특성 비교

항목전통 비밀번호패스키 (올바른 구현)패스키 (잘못된 구현)
피싱 저항성낮음높음 (도메인 바인딩)낮음 (origin 미검증 시)
크리덴셜 스터핑취약면역면역
서버 유출 영향치명적제한적 (공개키만 저장)치명적 (평문 ID 유출 시)
구현 복잡도낮음중간-높음높음 (오류 포함)
레거시 연동해당 없음주의 필요위험
세션 관리비교적 단순엄격한 설계 필요취약

잘 만든 패스키가 비밀번호보다 훨씬 안전한 건 맞아요. 그런데 잘못 만든 패스키는 비밀번호보다 나을 게 없고, “안전하다는 착각” 때문에 오히려 더 위험해요.

세션 토큰 수명도 빠뜨리면 안 돼요. 패스키로 강하게 인증했어도, 세션 토큰이 30일짜리 무기한 갱신이면 인증 강도가 그냥 희석돼요. 인증 강도에 맞춰 세션 수명도 짧게 설계해야 해요.


실무 개발자가 지금 당장 확인할 것들

단기 체크리스트 (1-3개월)

  • clientDataJSONtype, challenge, origin 세 가지 모두 검증하는지 확인
  • rpIdorigin 허용 목록을 화이트리스트로 엄격하게 관리
  • credentialId를 클라이언트 측에 평문으로 저장하지 않기
  • 비밀번호 로그인 경로가 남아 있다면, 패스키 전환 완료 후 비활성화 계획 수립

중장기 전략 (6-12개월)

  • WebAuthn Level 3 명세의 서버 측 검증 항목 전체 감사
  • 기기 분실·탈취 시 자격증명 폐기(Revocation) 플로우 구현
  • 패스키 보안 함정 관련 사내 구현 가이드라인 문서화

참고로, FIDO Alliance가 제공하는 오픈소스 webauthn-lib 레퍼런스 구현체를 직접 구현 코드와 비교 검토하는 걸 권장해요. 처음부터 직접 짜는 것보다 검증된 라이브러리를 쓰고 설정을 꼼꼼히 보는 편이 훨씬 안전해요.


패스키 시대, 어떻게 준비할까요?

정리하면 이렇게 돼요.

  • 패스키는 설계 자체는 견고하지만, 구현 오류가 치명적 취약점이 돼요
  • origin 검증 생략, 자격증명 ID 평문 저장, 레거시 비밀번호 병존이 주요 함정이에요
  • 잘못 만든 패스키는 “안전하다는 착각” 때문에 비밀번호보다 나쁠 수 있어요
  • 세션 토큰 수명과 폐기 플로우까지 패스키 설계의 일부로 봐야 해요

2026년 하반기로 갈수록 금융·공공 서비스의 패스키 의무화 압박은 커질 거예요. 그 속도에 맞춰 “일단 넣었다"는 식의 구현이 늘어날 가능성도 있어요.

패스키 보안 함정을 피하는 가장 확실한 방법은 하나예요. 명세를 끝까지 읽고, 검증 항목을 하나씩 테스트하는 것. 빠른 도입보다 올바른 도입이 먼저예요.

지금 운영 중인 서비스의 WebAuthn 구현 코드에서 origin 검증 로직을 한번 찾아보세요. 거기서 이 글의 가치가 결정돼요.

관련 글

참고자료

  1. 비밀번호 65년, 더 나은 기술이 있는데 왜 아직도 못 버리나 - 디지털포커스
  2. DB 암호화 CC인증 1호의 기술력…파라택시스이더리움, 금융·공공 석권
  3. 팩스 신청 방법과 유의사항: 꼭 알아야 할 5가지! - 정보투컴

Photo by Christopher Gower on Unsplash