Сообщить об ошибке.

Ограничение длины преобразования целочисленной строки в Python

CPython имеет глобальное ограничение на преобразование между int и str для смягчения атак типа "отказ в обслуживании". Это ограничение применяется только к десятичным или другим системам счисления, не равным степени двойки. Шестнадцатеричные, восьмеричные и двоичные преобразования не ограничены. Лимит можно настроить.

Новое в Python 3.11.

Тип int в CPython - это число произвольной длины, хранящееся в двоичной форме (обычно известное как "bignum"). Не существует алгоритма, который может преобразовать строку в двоичное целое число или двоичное целое число в строку за линейное время, если только основание не является степенью числа 2. Даже самые известные алгоритмы для основания 10 имеют субквадратичную сложность. Преобразование большого значения, такого как int('1' * 500_000), может занять больше секунды на быстром процессоре.

Ограничение размера преобразования предлагает практичный способ избежать CVE-2020-10735.

Ограничение применяется к количеству цифровых символов во входной или выходной строке, когда будет задействован алгоритм нелинейного преобразования. Подчеркивания и знаки не учитываются при подсчете лимита.

Когда операция превышает лимит, возникает ошибка ValueError:

>>> import sys
>>> sys.set_int_max_str_digits(4300)  # Illustrative, this is the default.
>>> _ = int('2' * 5432)
# Traceback (most recent call last):
# ...
# ValueError: Exceeds the limit (4300) for integer string conversion: 
# value has 5432 digits; use sys.set_int_max_str_digits() to increase the limit.
>>> i = int('2' * 4300)
>>> len(str(i))
# 4300
>>> i_squared = i*i
>>> len(str(i_squared))
# Traceback (most recent call last):
# ...
# ValueError: Exceeds the limit (4300) for integer string conversion: 
# value has 8599 digits; use sys.set_int_max_str_digits() to increase the limit.
>>> len(hex(i_squared))
# 7144

# Шестнадцатеричное число не ограничено.
>>> assert int(hex(i_squared), base=16) == i*i

Ограничение по умолчанию составляет 4300 цифр, как указано в sys.int_info.default_max_str_digits. Минимальное ограничение, которое можно настроить, составляет 640 цифр, как указано в sys.int_info.str_digits_check_threshold.

Проверка:

>>> import sys
>>> assert sys.int_info.default_max_str_digits == 4300, sys.int_info
>>> assert sys.int_info.str_digits_check_threshold == 640, sys.int_info
>>> msg = int('578966293710682886880994035146873798396722250538762761564'
...           '9252925514383915483333812743580549779436104706260696366600'
...           '571186405732').to_bytes(53, 'big')

Ограничение распространяется только на потенциально медленные преобразования между int и str или bytes:

  • int(string) с базой по умолчанию base=10.
  • int(string, base) для всех оснований, которые не являются степенью числа 2.
  • str(integer).
  • repr(integer).
  • любое другое преобразование строки с базой, например f'{integer}', '{}'.format(integer) или b'%d' % integer.

Ограничения не распространяются на функции с линейным алгоритмом:

  • int(string, base) с основанием 2, 4, 8, 16, or 32.
  • int.from_bytes() и int.to_bytes().
  • hex(), oct(), bin().
  • Mini-Language спецификации формата для шестнадцатеричных, восьмеричных и двоичных чисел.
  • преобразование str в float.
  • преобразование str в decimal.Decimal.

Рекомендуемая конфигурация.

Ожидается, что значение по умолчанию sys.int_info.default_max_str_digits подойдет для большинства приложений. Если приложению требуется другое ограничение, то необходимо установить его из основной точки входа с помощью независимого от версии кода Python, поскольку эти API-интерфейсы были добавлены в выпусках исправлений безопасности в версиях Python до 3.11.

Пример:

import sys
if hasattr(sys, "set_int_max_str_digits"):
    upper_bound = 68000
    lower_bound = 4004
    current_limit = sys.get_int_max_str_digits()
    if current_limit == 0 or current_limit > upper_bound:
        sys.set_int_max_str_digits(upper_bound)
    elif current_limit < lower_bound:
        sys.set_int_max_str_digits(lower_bound)

Если нужно полностью отключить его, установите его на 0.

Настройка лимита.

Перед запуском Python можно использовать переменную среды или флаг командной строки интерпретатора для настройки ограничения:

  • переменная среды PYTHONINTMAXSTRDIGITS=640 в Python3, установит ограничение на 640, или PYTHONINTMAXSTRDIGITS=0 Python3, отключит ограничение.
  • флаг командной строки интерпретатора python3 -X int_max_str_digits = 640, установит ограничение на 640.

Атрибут sys.flags.int_max_str_digits содержит значение PYTHONINTMAXSTRDIGITS или -X int_max_str_digits. Если установлены и env var, и опция -X, то опция -X имеет приоритет. Значение -1 указывает на то, что оба значения не установлены, поэтому во время инициализации использовалось значение sys.int_info.default_max_str_digits.

Из кода можно проверить текущий лимит и установить новый, используя эти системные API:

  • sys.get_int_max_str_digits() и sys.set_int_max_str_digits() являются геттером и сеттером для ограничения на уровне интерпретатора. У субинтерпретаторов есть свой предел.

Информацию о значении по умолчанию и минимуме можно найти в sys.int_info:

  • sys.int_info.default_max_str_digits - это скомпилированное ограничение по умолчанию.
  • sys.int_info.str_digits_check_threshold - это наименьшее допустимое значение ограничения (кроме 0, которое отключает его).

Внимание Установка нижнего предела может привести к проблемам. Хотя и редко, существует код, который содержит целые константы в десятичном виде в своем источнике, которые превышают минимальный порог. Следствием установки ограничения является то, что исходный код Python, содержащий десятичные целые литералы длиннее ограничения, столкнется с ошибкой во время синтаксического анализа, обычно во время запуска, во время импорта или даже во время установки - в любое время, когда обновленный файл .pyc еще не существует для кода. Обходной путь для источника, который содержит такие большие константы, состоит в том, чтобы преобразовать их в шестнадцатеричную форму 0x, поскольку она не имеет ограничений.

Тщательно протестируйте свое приложение, если используете низкий лимит. Убедитесь, что тесты выполняются с заранее установленным ограничением через среду или флаг, чтобы оно применялось во время запуска и даже на любом этапе установки, который может вызывать Python для предварительной компиляции исходных кодов .py в файлы .pyc.