유니코드 정규화해도 닮은 사용자 이름이 남는 이유

라틴 a와 키릴 а로 만든 두 사용자 이름이 화면에서는 똑같아 보이는데, NFC 정규화를 거쳐도 서로 다른 이름으로 가입된다. 이런 닮은꼴 이름은 무엇으로 걸러 낼까?

유니코드 정규화해도 닮은 사용자 이름이 남는 이유
세 줄 요약
  • NFC 정규화와 눈으로 보기에 비슷한 이름을 찾는 검사는 서로 다른 문제를 해결한다.
  • 유니코드 skeleton은 내부 유사성 비교값이며 로그인 ID나 표시 이름을 대신할 수 없다.
  • 다국어 이름 정책과 충돌 처리 방법을 정하고, 비교 라이브러리의 데이터 버전도 관리해야 한다.

같은 문자로 처리하는 것과 닮은 글자를 찾는 것은 다르다

라틴 문자 a(U+0061)와 키릴 문자 а(U+0430)는 비슷하게 보여도 다른 문자다. 둘을 NFC로 정규화해도 같은 코드 포인트가 되지 않는다. 회원 이름에 정규화를 적용했는데도 닮은 이름이 등록되는 것은 이 차이 때문이다.

Unicode의 정규화 설명에서 NFC가 맞추는 것은 정준적으로 같은 표현이다. 예를 들어 é 한 문자와 e 뒤에 결합 악센트를 붙인 표현은 NFC에서 같아진다. 서로 다른 문자의 생김새까지 합치는 절차는 아니다.

비교 대상NFC 비교 결과해결하는 문제
é와 e + 결합 악센트같아진다.같은 문자의 다른 인코딩 표현이다.
라틴 a와 키릴 а다른 상태로 남는다.시각적 유사성을 따로 검사해야 한다.

NFKC는 호환 표현의 차이까지 줄이지만 정보를 없앨 수 있다. 단순히 더 강한 정규화를 고른다고 사용자 이름 사칭 검사가 완성되지는 않는다. 서비스가 어떤 차이를 같은 이름으로 취급할지와, 사람이 혼동할 이름을 어떻게 제한할지는 별도로 정해야 한다.

닮은 이름을 찾은 뒤 기존 계정으로 바꾸면 안 된다

UTS #39는 문자열의 시각적 혼동 가능성을 비교하는 skeleton을 정의한다. 같은 비교값이 나오는 이름을 찾는 데 쓰는 값이며 사용자에게 보여 주거나 식별자의 정규형으로 사용하기 위한 값이 아니다.

따라서 새 이름이 기존 이름과 같은 skeleton을 갖더라도 기존 계정의 ID로 바꿔 로그인시키는 방식은 맞지 않는다. 등록을 거절할지, 다른 이름을 제안할지, 검토 대상으로 보낼지는 서비스의 등록 정책으로 결정할 수 있다. 이 판단은 계정이 같은지를 판정하는 인증 규칙과 분리해야 한다.

두 이름 비교와 이름 하나의 의심 신호 검사를 나눈다

ICU4J의 SpoofChecker는 두 문자열을 비교하는 areConfusable과 문자열 하나를 설정한 규칙에 따라 점검하는 failsChecks를 제공한다. 전자는 기존 이름과 닮았는지를, 후자는 허용 문자나 문자군 제한에 걸리는지를 확인할 때 쓰인다.

기존 이름이 많으면 각 이름의 getSkeleton 결과를 집합으로 구성해 새 이름과 비교하는 방식도 공식 예제에 있다. 다만 ICU는 버전이 달라지면 skeleton 결과가 바뀔 수 있다고 명시한다. 오래 유지되는 데이터베이스 키로 고정하기보다 실행 시 계산하는 방식을 권고한다.

이 구분을 적용하면 회원가입 처리를 두 질문으로 나눌 수 있다. 먼저 계정 이름 규칙에 맞는지 검사하고, 이어 기존 이름이나 보호할 이름과 시각적으로 혼동되는지 검사한다. 어느 단계의 실패인지 구별해야 정상적인 다국어 이름까지 일괄 차단하는 일을 줄일 수 있다.

한글과 영문이 섞였다고 모두 차단할 이유는 없다

문자군이 섞였다는 사실 하나로 사칭을 확정할 수는 없다. UTS #39의 Highly Restrictive 분류에도 라틴 문자·한자·한글 조합이 들어간다. 물론 식별자 문법과 적용 중인 허용 문자 프로필을 함께 충족해야 한다.

RFC 8265는 국제화된 사용자 이름에 대소문자를 맞추는 프로필과 보존하는 프로필을 구분하고, 이름 비교에서 잘못된 허용과 거절이 모두 보안 문제를 만들 수 있다고 설명한다. NFC 함수 하나만 추가하는 것으로 이런 이름 정책 전체를 구현했다고 볼 수 없다.

서비스의 다음 결정은 ‘모든 이름을 비슷한 영문으로 바꾸기’가 아니다. 허용할 이름과 같은 계정으로 취급할 범위를 정한 뒤, 유사성 검사를 등록 정책에 연결하는 일이다. 정규화된 계정 이름과 사칭 탐지용 비교값의 역할을 분리해야 두 문제를 각각 다룰 수 있다.