Python str.lower()의 Unicode 버전 차이가 IDNA 2003 불일치를 만들어 CVE-2026-17084로 수정됨
- Python의 IDNA 2003 코덱은 RFC 3454가 고정한 Unicode 3.2.0 대소문자 규칙을 써야 하지만,
stringprep의 B.3 처리에서 인터프리터 최신 Unicode 데이터를 따르는str.lower()를 호출해 명세와 달라졌음 - 이 불일치로 체로키 문자 U+13A0 두 글자의 IDNA 결과가 RFC 3454 기준
xn--58da와 최신 Unicode 17.0.0 기준xn--kz9aa로 갈리며, CVE-2026-17084가 부여됨 - CPython은 현재 내장 Unicode 데이터와 ucd_3_2_0의
lower()결과가 다른 모든 코드포인트를 조사해 B.3 예외 테이블에 추가함으로써 IDNA 2003 결과를 고정함 - Python 표준
str.encode('idna')는 IDNA 2003을 지원하며, 글은 일반 용도에서는 PyPI의idna패키지로 IDNA 2008을 사용하라고 권고함 - 해커뉴스 토론에서는 필터와 실제 요청 처리기가 같은 입력을 서로 다른 호스트명으로 해석할 때 SSRF 차단이나 신뢰 호스트 검사 우회로 이어질 수 있다는 의견이 나왔음
Hacker News opinions
이게 잘못된 데이터를 만드는 버그가 아니라 실제 취약점인 이유가 더 설명됐으면 함. 공격자가 쓸 만한 경로가 바로 보이지 않음.
작성자인데, 한 시스템 안에서 IDNA 2003 구현이 둘로 갈리면 공격 가능함. 정책이나 필터는 호스트명을 한쪽 방식으로 보고, 실제 동작 부분은 다른 방식으로 보면 됨. URL 파서 차이를 노린 SSRF와 비슷한 유형임.
결국 같은 문자열을 서로 다른 호스트명으로 해석하는 파서 차이임. 신뢰하거나 차단한 호스트명, 관리자 호스트명, SSRF 필터가 정확한 비교에 의존하니 문제가 될 수 있음. 다만 IDN을 다루는 상황이어야 해서 조건은 꽤 한정적임.
비밀번호 재설정 메일 같은 데서도 상상해볼 수 있음. 정상이라면 존재하지 않아 실패할 유니코드 도메인 변형을 공격자가 등록하면, 재설정 요청을 가로챌 여지가 생김.
정확성 버그를 보안 문제로 먼저 취급하는 이유가 있음. Chromium에서는 WebAudio의 부동소수점 flush-to-zero가 V8 힙 OOB 접근으로 이어진 사례도 있었음. 서로 다른 구현이 불일치하는 지점은 대개 공격 사슬의 출발점임.
TLS SAN의 dNSName은 사람이 읽는 유니코드 텍스트로 디코딩하면 안 된다고 봄. 와일드카드 하나를 제외하면 DNS 이름은 바이트가 같은지만 비교해야 함. Python 쪽에서 유니코드로 다루려 했던 흐름은 위험했음.
IDN을 호스팅한다면 .lower() 차이를 악용해 등록한 도메인이 있는지 찾아봐야 할 듯함.
Python에서 ß.upper()가 SS가 되는 것도 처음엔 놀랐음. 대소문자 변환 뒤 문자열 길이가 바뀔 수 있는데, Unicode SpecialCasing 규칙에 있는 동작임.
str.lower() 자체가 취약점이라는 표현은 과장 같음. 명세를 따르지 않은 IDNA 2003 구현이 취약점인 거지 lower()가 문제인 건 아님.
한 시스템에 IDNA 2003 구현이 두 개나 실제로 들어가는 경우가 흔한지 모르겠음. 그런 사례가 있어야 공격 표면도 생기는 거 아닌가.
현재 Unicode와 3.2의 차이를 모든 코드포인트에서 찾아 예외 테이블로 넣는 방식은 꽤 임시방편처럼 들림. Unicode 3.2 전용 lower 구현을 따로 두는 편이 더 자연스러워 보임.
예전에 Kelvin sign 때문에 비슷한 문제를 겪었음. 원인 찾는 데 생각보다 오래 걸리더라.