Tin tức

str.lower() của Python là lỗ hổng bảo mật trong IDNA 2003

Một lỗi tinh vi trong triển khai IDNA 2003 của CPython đã sử dụng phiên bản Unicode của trình thông dịch để chuyển đổi chữ hoa/thường, khác với đặc tả RFC 3454. CVE-2026-17084 khắc phục bằng cách thêm các ngoại lệ để khớp với hành vi Unicode 3.2.0.

August 26, 2026· 3 min read· Nguồn: sethmlarson.dev
str.lower() của Python là lỗ hổng bảo mật trong IDNA 2003

str.lower() của Python là một công cụ đắc lực — nhưng khi được dùng bên trong codec IDNA 2003, nó có thể mở ra lỗ hổng bảo mật. Vấn đề: IDNA 2003, được định nghĩa bởi RFC 3491, dựa trên StringPrep (RFC 3454), vốn chỉ định việc chuyển đổi chữ hoa/thường dựa trên Unicode 3.2.0. Tuy nhiên, str.lower() của Python lại dùng phiên bản Unicode mà trình thông dịch đi kèm — hiện tại là 17.0.0. Sự không khớp này có nghĩa codec có thể tạo ra các tên miền khác với đặc tả, có khả năng dẫn đến nhầm lẫn hoặc giả mạo.

Đoạn mã dễ bị tấn công trong mô-đun stringprep của CPython trông như thế này:

def map_table_b3(code):
    r = b3_exceptions.get(ord(code))
    if r is not None: return r
    return code.lower()

Lệnh gọi str.lower() sử dụng dữ liệu Unicode của trình thông dịch, không phải dữ liệu Unicode 3.2.0 mà StringPrep yêu cầu. Python thực sự đi kèm mô-đun unicodedata.ucd_3_2_0 dành riêng cho mục đích này, nhưng mã đã không dùng nó để chuyển đổi chữ hoa/thường.

Tác động thực tế: một số ký tự Unicode, như chữ Cherokee 'Ꭰ' (U+13A0), chuyển đổi khác nhau dưới Unicode 17.0.0 so với 3.2.0. Điều này thay đổi đầu ra mã hóa tương thích ASCII (ACE):

# Giá trị tuân thủ RFC 3454
>>> "ᎠᎠ".encode("idna")
'xn--58da'

# Giá trị nếu dùng chuyển đổi chữ hoa/thường Unicode 17.0.0
>>> "ᎠᎠ".encode("idna")
'xn--kz9aa'

Sự khác biệt đó có nghĩa hai chuỗi Unicode khác nhau có thể ánh xạ đến cùng một nhãn IDNA, hoặc cùng một chuỗi có thể ánh xạ đến các nhãn khác nhau tùy thuộc vào phiên bản Python — một công thức kinh điển cho các cuộc tấn công nhầm lẫn tên miền.

Bản sửa lỗi, được hợp nhất trong CPython PR #155293, thêm các ngoại lệ rõ ràng cho mọi điểm mã nơi str.lower() khác với hành vi Unicode 3.2.0. Giờ đây IDNA 2003 nhất quán với đặc tả, bất kể phiên bản Unicode của trình thông dịch.

Điều này được báo cáo bởi Bitshift, với sự đồng phát triển của Stan Ulbrych, và được đánh giá bởi Marc-Andre Lemburg và Petr Viktorin. Được theo dõi là CVE-2026-17084.

Đối với hầu hết nhà phát triển, bài học rút ra là: hãy dùng gói idna (IDNA 2008) thay vì str.encode('idna') (IDNA 2003). Nhưng nếu bạn phải hỗ trợ IDNA cũ, bản sửa lỗi này rất quan trọng.

Lệnh gọi str.lower() trong hàm này là một lỗ hổng!
Ban biên tập Manul X
IDNA 2003 so với IDNA 2008
So sánh nhanh
Khía cạnhIDNA 2003IDNA 2008
Đặc tảRFC 3491 (StringPrep)RFC 5890-5893
Phiên bản Unicode3.2.0Mới nhất (ví dụ: 15.1)
Hỗ trợ Pythonstr.encode('idna')gói idna
Chuyển đổi chữ hoa/thườngStringPrep B.2/B.3UTS #46
Trạng thái bảo mậtLỗi thời, có CVE-2026-17084Được bảo trì tích cực