1P by GN⁺ | ★ favorite | 댓글 1개
  • 스위스 정부 로그인 시스템 AGOV의 6자리 이메일 인증 입력창이 프랑스식 AZERTY 키보드의 숫자 입력을 처리하지 못해 계정 등록 자체가 막힘
  • 입력창의 JavaScript가 키 입력을 가로채 인증 코드를 별도 변수에 저장하면서 Shift 키를 무시했고, 숫자 입력에 Shift가 필요한 AZERTY 배열을 사실상 차단함
  • Firefox·Chromium·Chrome·Safari와 Linux·macOS의 여러 조합에서 같은 오류가 발생했지만 QWERTY 키보드에서는 정상 작동해, 브라우저나 운영체제가 아닌 키보드 배열 문제로 좁혀짐
  • 공식 지원도 동일한 이메일 인증을 통과해야 이용할 수 있고 이메일·전화 같은 대체 수단이 없어, 로그인하지 못하는 사용자는 로그인 버그를 신고할 수도 없었음
  • 네이티브 입력 필드 대신 복잡한 키 처리 로직을 사용하고 제품과 지원 채널을 같은 인증 절차에 묶으면 접근성과 장애 대응이 함께 무너지므로, 단순한 DOM 기반 입력과 독립적인 지원 채널이 필요함

디지털 신원 인프라가 된 AGOV

이메일 인증 단계에서 멈춘 등록

  • AGOV 등록 절차는 이메일 주소를 제출한 뒤 6자리 인증 코드를 입력하는 것으로 시작함
  • 인증 코드 UI는 숫자마다 하나씩 총 6개의 입력 상자로 구성됨
    • 숫자를 입력해도 포커스가 다음 상자로 이동하지 않음
    • 현재 상자에 숫자가 계속 쌓이며 빨간색 오류 상태로 바뀜
    • 한 자리씩 수동으로 이동해 입력해도 마지막에는 field required 오류가 발생함
  • 개발자 도구에서 DOM 값을 직접 수정하려 했지만 눈에 띄는 폼 값이 없었고, 웹 콘솔에도 오류가 기록되지 않음
  • 다른 이메일 주소나 test@example.com 같은 가짜 주소를 사용해도 결과는 같았음
    • example.comRFC 6761에 따라 예약된 도메인임

브라우저와 운영체제 가능성 배제

  • 처음에는 Firefox와 Linux 조합이 지원되지 않거나 CAPTCHA가 실패한 것으로 의심함
    • 인증 코드 폼이 나타나기 직전에 eu-api.friendlycaptcha.eu에 연결함
    • 공식 요구사항 문서는 지원 운영체제로 Windows와 macOS만 기재함
    • 반면 보안 키 안내에는 Linux와 Firefox도 지원한다고 나옴
  • 다음 6개 환경 조합에서 모두 같은 오류가 발생함
    • Linux의 Firefox, Chromium, Chrome
    • macOS의 Firefox, Safari, Chrome
  • 업무용 노트북에서는 입력 후 자동으로 다음 상자로 이동했고, 빈 코드가 아닌 잘못된 코드라는 Code entered is incorrect 오류도 정상적으로 나타남
  • 친구의 macOS 컴퓨터에서도 세 브라우저 모두 인증 코드를 받아들였음
  • 정부 서비스와 장기적으로 상호작용하기 위해 고용주가 지급한 컴퓨터에 의존할 수는 없었으며, 업무용 컴퓨터에서 등록하더라도 개인 노트북에서 로그인하지 못할 가능성이 남아 있었음

문제 신고까지 막은 지원 구조

  • 공식 지원은 웹 폼으로만 제공됐으며, 이 폼도 먼저 이메일 인증 코드를 입력해야 했음
  • FAQ는 참여 기관 지원이 업무 시간 중 온라인 티켓 생성으로만 제공된다고 명시함
  • Swiss Federal Chancellery의 우편 주소 외에는 이메일이나 전화번호가 없었음
  • AGOV에 칭찬·비판·요청을 남기는 피드백 기능도 AGOV 또는 동등한 계정 로그인을 요구함
  • 이메일 인증에 실패한 사용자는 같은 인증 절차 때문에 문제를 신고할 수도 없는 순환 의존성에 빠짐
  • AGOV Access와 지원 기기

    • AGOV Access Android 앱의 평점은 1.4점이었지만, Google Play 리뷰에서 같은 인증 코드 문제는 찾지 못함
    • 리뷰에는 보안·프라이버시 중심 Android 파생 운영체제 GrapheneOS 지원 요청이 다수 있었음
    • 공식 FAQ에 따르면 무단 변경 방지용 강화 프레임워크가 GrapheneOS와 호환되지 않아 앱이 작동하지 않음
    • Federal Chancellery는 공급업체에 호환성 확보를 지시함
    • 현재 등록과 로그인의 2차 인증 수단은 허용된 기본 iOS·Android 스마트폰 또는 보안 키이며, 어느 쪽이든 먼저 이메일 인증을 통과해야 함

JavaScript에서 발견한 원인

  • 페이지가 불러오는 리소스는 적었지만, 난독화되지 않은 최소화 상태의 주 JavaScript 파일은 2.4MB였고 풀어 쓰면 10만 줄이 넘었음
  • field required 문자열을 검색해 인증 코드 상태를 설정하는 로직을 찾음
    • 인증 코드 변수가 없으면 상태를 REQUIRED로 설정함
    • 길이가 요구된 코드 길이와 다르면 WRONG으로 처리함
    • 길이가 맞으면 서버의 이메일 인증 결과에 따라 VALID 또는 오류 상태를 사용함
  • verificationCodeEntered 함수는 키 입력을 가로채 인증 코드를 JavaScript 변수에 수집함
    • Enter는 인증 콜백을 실행함
    • Backspace는 현재 값을 지움
    • 방향키와 Tab은 입력 상자 사이를 이동함
    • 일반 키는 Number(S.key)로 숫자화한 뒤 코드에 저장함
    • Control, Meta, Shift, v, r 등의 키는 처리하지 않고 반환함
  • 폼 제출 시 DOM 값을 읽지 않기 때문에 개발자 도구나 브라우저 확장 기능으로 입력 상자를 수정해도, JavaScript가 별도로 관리하는 상태에는 반영되지 않음

AZERTY에서만 숫자가 차단된 이유

  • 프랑스 표준 AZERTY 배열은 숫자를 입력할 때 Shift 키가 필요함
  • 코드가 Shift 입력을 무시했기 때문에 AZERTY에서는 숫자 자체를 인증 코드로 저장할 수 없었음
  • 업무용 컴퓨터는 영문 QWERTY 배열이었고, 문제를 확인해 준 사람 중 프랑스인은 없었음
    • 그 결과 테스트한 기기 중 개인 노트북 두 대만 고장 난 것처럼 보였음
  • 운영체제에서 키보드 배열을 다른 배열로 바꾸자 개인 노트북에서도 입력이 정상화됨
    • 반대로 정상 작동하던 기기를 프랑스식 배열로 전환하자 같은 오류가 재현됨
  • 스위스의 표준 프랑스어 키보드는 중앙유럽에서 흔한 QWERTZ 계열임
    • 프랑스어와 독일어를 효율적으로 입력할 수 있고 숫자 입력에 Shift가 필요하지 않아 같은 오류가 나타나지 않음
  • Swiss Federal Statistical Office 기준 스위스에는 프랑스 국적 거주자가 약 17만1,000명 있으며, 스위스 정부와 상호작용해야 하는 국경 통근자는 이 수치에 포함되지 않음

소스 공개 전까지의 조사 제약

  • AGOV FAQ에 따르면 AGOV 소스 코드는 법적 요구사항을 충족하기 위해 2026년 12월 공개될 예정임
  • EMBAG 제9조는 연방 기관이 업무 수행을 위해 직접 개발하거나 개발을 맡긴 소프트웨어의 소스 코드를 공개하도록 요구함
    • 제3자의 권리나 보안상 사유가 공개를 방해하거나 제한할 때는 예외가 적용됨
  • 공개 대상은 AGOV e-ID Verifier를 포함해 프로젝트 목표를 달성한 AGOV 버전이며, 이후 버전은 릴리스 후 준비되는 릴리스 노트로 공개될 예정임
  • 일부 서비스가 이미 AGOV를 의무 로그인으로 사용하지만 소스는 아직 공개되지 않아, 사용자가 개발자에게 직접 버그를 알리거나 우회 방법을 공유하기 어려웠음

등록 후 확인한 신원 검증 범위

  • 키보드 배열을 변경한 뒤 이메일 주소를 인증하고 보안 키로 등록을 완료함
  • 등록 과정에서 이름, 전화번호, 생년월일을 입력했지만 이 정보들은 검증되지 않았음
  • 기본 등록 단계에서 확인된 것은 두 가지뿐이었음
    • 이메일 주소가 실제로 존재함
    • 2차 인증 수단이 허용된 모델의 보안 키이거나, 허용된 Android·iOS 기기에서 실행되는 AGOV Access 앱임
  • AGOV는 원격 피싱에 의한 계정 탈취에는 견고하지만, 다른 사람이 타인의 이름과 신원 정보를 사용해 계정을 만드는 행위를 기본 등록 단계에서 막지는 않음
  • 추가 확인 결과, 민감한 것으로 분류된 서비스에 접근할 때는 신원 검증이 필요함
    • 등록 직후에는 아직 그런 서비스에 접근하지 않은 상태였음
  • 지원 폼으로 버그를 신고했지만 답변은 받지 못함

설계에서 얻은 교훈

  • 견고하지 못한 입력과 지원 설계

    • 6개의 입력 필드와 복잡한 JavaScript 로직으로 화려한 UI를 만드는 방식은 견고하지 않음
    • 네이티브 브라우저 입력 필드 하나를 사용하고 CSS로 시각적 형태만 꾸미는 편이 나음
    • 폼 로직을 DOM과 완전히 분리하면 제출 시 DOM 값을 읽을 수 없고, 고급 사용자나 브라우저 확장 기능도 입력값을 수정할 수 없음
    • 지원 채널이 하나뿐이면 그 채널 자체가 고장 났을 때 연락할 방법이 사라지므로 이메일이나 전화 같은 두 번째 채널이 필요함
    • 본 제품과 지원 채널에 같은 로그인 로직을 적용하면 로그인할 수 없는 사용자의 버그 신고를 놓치게 됨
    • 여러 인터넷 서비스가 상태 페이지를 별도 도메인에 두는 것도 같은 이유임
    • 지원 요청을 이메일 인증 뒤에 두면 스팸은 줄일 수 있지만 실제 사용자의 장애 신고도 함께 줄어듦
    • 일부 시스템에서 사용을 의무화한 뒤에야 소스 코드를 공개하면 사용자의 문제 해결과 버그·우회 방법 공유가 제한됨
    • 법적 소스 공개는 단순한 준수 항목을 넘어 실제 장애 조사에도 유용함
  • 조사에 도움이 된 오픈 웹

    • 관련 JavaScript 로직이 난독화되지 않아 몇 차례 문자열 검색만으로 원인을 찾을 수 있었음
    • 고급 디컴파일러나 값비싼 LLM은 필요하지 않았음
    • 코드가 난독화됐거나 WebAssembly로 컴파일됐다면 원인 파악에 훨씬 더 많은 시간이 들었을 것임
    • 코드가 승인된 폐쇄형 운영체제에만 배포되지 않고 오픈 웹에서 제공돼, 최소화된 형태라도 내려받아 조사할 수 있었음
  • 원인 규명을 가능하게 한 조건

    • 서로 다른 키보드 배열을 사용하는 여러 노트북을 비교해 브라우저별 문제나 네트워크 문제 가능성을 일찍 낮출 수 있었음
    • 다만 사용자 실수가 아님을 확인하기 위해 각 브라우저 조합의 화면을 체계적으로 캡처하느라 초기 증거 수집에는 시간이 걸림

댓글과 토론

Lobste.rs 의견들
  • 인터넷에서 특히 싫어하는 관행 중 하나는 웹사이트가 키 입력을 가로채 제멋대로 처리하고 브라우저에 그대로 전달하지 않는 것임
    비밀번호 필드에 복사·붙여넣기를 금지하는 사이트가 훨씬 흔하지만, 이것도 같은 부류이며 어쩌면 더 심각해 보임

    • 브라우저가 HTML과 CSS만으로 기본 지원하는 기능을 굳이 JavaScript로 다시 구현할 이유를 모르겠음
      대개 억지스러운 이유로 복잡성만 늘어남
  • 문자마다 입력 필드를 하나씩 두는 방식은 전염병과도 같음
    어느 회사가 멋져 보인다고 도입하자 이제 모두가 각자 자기 버전을 구현하고 있음

    • 이런 UI가 일회용 코드 입력 성공률을 실제로 높여도 놀랍지 않을 듯함
      사용자가 자세히 읽지 않아도 비밀번호 입력란이 아니라는 사실을 즉시 알아차릴 수 있음
  • 관련 사례로 Medium once had a bug like this which prevented entering the character 'Ś'가 있음

  • 다른 구현법을 생각해 봤지만 결국 평범한 <input type="text" />보다 나은 방법을 찾지 못하겠음
    일반적인 입력란과 너무 다르게 보이게 만드는 CSS 장식조차 망설여짐. 입력값을 큰 칸들에 복제해 보여줄 수도 있지만 사용자가 허용 길이보다 많이 입력하면 어떻게 할지 등 온갖 문제에 답해야 하고, 그 수고를 감수할 만큼 이점이 크지 않음. 많아야 입력란의 글꼴과 크기 정도만 바꾸겠음

    • OTP에서 가장 흔한 오류는 1234 대신 1224처럼 숫자 하나를 잘못 입력하는 것임
      두 번째 2를 클릭하고 3을 누르는 방식이 가장 빠른 수정법이지만 HTML+CSS만으로는 구현할 수 없음
    • 아주 우스운 발상이라는 점은 인정하지만, 하나의 <input type="text" pattern="[0-9]+" minlength=6 maxlength=6> 에 특이한 자간을 적용해 시각적으로 여섯 칸처럼 보이게 만들 수 있을지 궁금함
      더 쉬운 방법은 사용자가 단일 텍스트 입력란에 입력하면 JavaScript가 DOM의 다른 위치에 여섯 칸을 그리고, 키를 누를 때마다 발생하는 input 이벤트로 갱신하는 것임
      어느 쪽이 접근성에 더 나을지는 확신하기 어려움. 화면 낭독기 관점에서는 평범한 <input>과 장식용 <canvas alt="">를 함께 쓰는 후자가 오히려 나을 수 있고, 키보드 조작도 이상해지지 않음. 하지만 화면 낭독기 외의 보조 수단을 쓰는 사용자에게는 <input>을 시각적으로 숨기는 것이 접근성 참사가 될 수 있어 매우 조심스러움
  • 많은 서비스처럼 이메일에 코드와 활성화 링크를 함께 제공해도 됨

    코드 입력: XX XX XX
    
    또는 링크 클릭: <리디렉션 + 코드 활성화 링크>  
    

    이것도 같은 문제를 해결할 수 있음

    • 이메일의 링크를 클릭하도록 사용자를 길들이는 것은 피싱 방어를 후퇴시키는 일이라고 봄
    • 노트북에서 웹사이트를 열고 휴대전화로 이메일을 받았다면 복사·붙여넣기가 그리 편하지 않음
      이를 예외적인 상황이라고 생각할 수도 있지만, 접근성이란 바로 그런 예외를 제대로 처리하는 것임
  • Tridactyl에도 정확히 같은 문제가 있음 https://github.com/tridactyl/tridactyl/issues/3257
    2019년에 처음 보고됐는데, 이번 일을 계기로 부끄러워서라도 고치게 될지 모르겠음

  • 그냥 작동하는 웹사이트보다 망가지거나 느린 웹사이트를 만드는 데 훨씬 더 많은 노력이 든다는 사실을 또다시 보여줌