Google Authenticator와 호환되는 여러 앱(Aegis, Raivo OTP, 2FAS)은 저장된 2FA 계정을 “내보내기” 하여 새 기기로 옮길 수 있게 합니다. 내보내기는 otpauth-migration://로 시작하는 URI가 담긴 QR 코드 형태를 띱니다. 그 URI 안에는 모든 계정을 한 번에 담은 단일 프로토콜 버퍼 번들(Google의 MigrationPayload 스키마)이 들어 있습니다 — 발급자, 계정 이름, 비밀 시드, 알고리즘, 자릿수, 유형(HOTP / TOTP), 그리고 카운터.
이 형식은 원래 문서화되지 않았지만, 리버스 엔지니어링되어 현재 2FA 백업의 사실상 표준 교환 형식입니다. 서드파티 인증기 앱들도 동일한 QR을 인식하고 가져옵니다.
QR 하나에 수십 개의 계정이 담길 수 있습니다. QR은 일반 TOTP 설정 QR과 시각적으로 구분할 수 없습니다 — 그것이 전체 번들을 담고 있다고 일반 관찰자에게 알려주는 시각적 표시가 없습니다.
단일 번들 QR이 유독 위험한 이유
일반적인 otpauth:// 설정 QR은 요청하지 않았는데 스캔하면 위험하지만, 피해 범위는 계정 하나입니다. 마이그레이션 QR의 피해 범위는 인증기에 있는 모든 계정입니다. 공격자가 이 QR이 표시된 화면을 사진으로 찍는다면 — 커피숍에서 어깨너머로, 눈치채지 못한 CCTV 카메라를 통해, 투명한 유리창을 통해 — 그가 포함된 모든 계정의 2FA를 우회할 수 있게 됩니다. 영구적으로요. 각 비밀 키를 수동으로 교체할 때까지 그렇습니다. 대부분의 서비스는 이를 쉽게 해주지 않습니다.
번들 안의 비밀 키에는 만료가 없습니다. 세션 토큰(만료됨)과 달리, passkey QR(일회용)과 달리, 마이그레이션 번들의 시드는 서비스별로 하나씩 교체할 때까지 유효합니다.
그래서 저희 스캐너의 모든 otpauth-migration:// QR에 대한 판정은 likely_dangerous에서 시작하며 상황과 무관하게 그 상태를 유지합니다. 형식 자체는 괜찮습니다. 위협 모델은 그 형식의 존재 자체입니다.
base64 본문은 디코딩되면 다음과 같은 최상위 필드를 가진 MigrationPayload 프로토콜 버퍼입니다:
otp_parameters — 반복됨. 계정당 항목 하나.
version — 스키마 버전.
batch_size — 내보내기가 여러 QR로 분할될 때의 전체 청크 수.
batch_index — 이 청크의 위치.
batch_id — 청크들을 함께 연결하는 고유 ID.
각 otp_parameters 항목에는 다음이 있습니다:
secret — 원시 시드 바이트. 이것이 실제 키 자료입니다.
name — 계정 라벨(예: alice@acme.com).
issuer — 서비스 이름(예: ACME, GitHub).
algorithm — SHA1 / SHA256 / SHA512 / MD5.
digits — 코드당 6자리 또는 8자리.
type — TOTP(시간 기반) 또는 HOTP(카운터 기반).
counter — HOTP 항목의 현재 카운터 값.
저희 스캐너가 보여주는 것, 그리고 의도적으로 보여주지 않는 것
✓ 표시됨
번들의 모든 항목에 대해, 판정은 발급자(ACME, GitHub, AWS), 계정 이름(alice@acme.com), 알고리즘(SHA1), 자릿수(6), 그리고 유형(TOTP)을 보여줍니다. 판정 공개에는 “이 번들의 권한: ACME / alice@acme.com; GitHub / bob@github; AWS / root; …”라고 표시되어, 마이그레이션 중인 사용자가 가져오기 전에 목록을 점검할 수 있습니다.
스키마 버전, 배치 인덱스, 배치 크기, 배치 ID도 표시됩니다 — 내보내기가 여러 QR로 청크되어 있을 때 유용합니다.
✗ 절대 디코딩하지 않음
원시 secret 바이트는 절대 판정 출력으로 디코딩되지 않습니다. 저희 분석기는 각 항목이 올바른 형식인지(비밀 키 존재 + 비어 있지 않음) 확인하기 위해서만 읽고, 결과를 구성하기 전에 그 값을 폐기합니다.
이는 테스트 스위트로 보장됩니다 — 저희는 분석기에 카나리 문자열(SECRET_SEED_1, SECRET_SEED_2)을 포함한 직접 만든 protobuf 페이로드를 넣고, 그 문자열이 직렬화된 판정 출력에 절대 나타나지 않음을 확인합니다. 거기서 회귀가 발생하면 CI가 실패합니다.
언제(그리고 어떻게) 사용해야 하는가
정당한 사용 사례: 한 휴대폰에서 다른 휴대폰으로 업그레이드하는 경우입니다. 옛 휴대폰이 화면에 마이그레이션 QR을 약 30초간 표시합니다. 새 휴대폰의 인증기 앱이 그 화면을 겨누어 가져옵니다. 방에 다른 누구도 없습니다. 모든 것이 가져와졌음을 확인한 후 옛 휴대폰에서 내보내기를 삭제합니다.
위험 표면은 다음과 같습니다:
QR 스크린샷. 클라우드 동기화 사진에 저장된 스크린샷은 해당 계정에 동기화된 모든 기기에 QR을 올려놓습니다.
채팅으로 QR 공유. 잠깐이라도, 채팅에 접근할 수 있는 사람은(지금이든 나중이든) 번들을 추출할 수 있습니다.
도움말 포럼 스레드에 QR 게시. 생각보다 자주 발생합니다. 사람들이 도움을 요청하면서 실수로 자신의 인증기 내보내기 사진을 올립니다.
설정 QR로 오인된 공개 포스터/표지판. 이 형식은 단일 계정 설정 QR과 시각적으로 구분할 수 없습니다.
마이그레이션 QR이 노출되었다고 의심되면
번들 안의 모든 계정에 걸친 자격 증명 유출로 취급하세요. 복구 단계는 순서대로 다음과 같습니다:
영향받은 각 서비스에 로그인하여 2FA 비밀 키를 교체하세요. 대부분의 서비스는 계정 → 보안 → 2단계 인증 → 비활성화 후 재등록에서 이를 할 수 있게 합니다.
교체 기능을 제공하지 않는 서비스(드물지만 존재함)의 경우, 계정을 제거하고 다시 추가하세요.
영향받은 모든 계정의 최근 로그인 활동을 점검하세요.
원본 내보내기 QR을 보유하고 있을 수 있는 모든 기기와 클라우드 동기화 사진 라이브러리에서 삭제하세요.
번들을 가진 공격자는 사용자가 교체할 때까지 수년간 유효한 2FA 코드를 생성할 수 있습니다. 미루지 마세요.