Gejala Masalah pada Pipeline Data dan Payload JSON

Saat memproses data analitik, kalkulasi kriptografi, atau payload JSON berukuran besar, pipeline backend Python dapat mendadak berhenti dengan traceback berikut:

ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit

Error ini umumnya muncul pada fungsi konversi eksplisit seperti int("123..."), fungsi bawaan str(large_int), serta modul standard parser seperti json.loads() jika terdapat angka integer mentah melebihi 4.300 digit.

Root Cause: Mitigasi CVE-2020-10735

Secara default, representasi internal integer CPython mengalokasikan digit secara dinamis (arbitrary-precision integer). Namun, konversi antara basis biner internal CPython dan basis desimal memiliki kompleksitas algoritma kuadratik O(N^2), dengan N adalah jumlah digit.

Karakteristik komputasi ini membuka celah keamanan Denial of Service (DoS) berbasis CPU, yang dicatat dalam CVE-2020-10735. Penyerang dapat mengirim payload HTTP JSON kecil berisi integer puluhan ribu digit desimal untuk memonopoli 100% core CPU backend selama detik hingga menit per request. CPython menerapkan batasan ketat konversi desimal secara global dengan ambang batas default 4.300 digit (sekitar 14.284 bit) mulai rilis patch versi 3.11, 3.10.7, 3.9.14, 3.8.14, dan 3.7.14.

Karakteristik Batasan

  • Hanya berdampak pada konversi antara basis 10 (desimal) dan integer biner internal (seperti str(), int(str, 10), repr(), format()).
  • Operasi aritmetika murni seperti 10**100000 tetap valid dan tidak memicu error.
  • Konversi berbasis eksadesimal (basis 16), oktal (basis 8), atau biner (basis 2) via bin(), hex(), atau int(s, 16) tidak dibatasi karena kompleksitas komputasinya linier O(N).

Reproduksi Masalah

Contoh eksekusi script minimal yang memicu error:

# Reproduksi kegagalan konversi desimal > 4300 digit
payload = "9" * 4301

try:
    val = int(payload)
except ValueError as err:
    print(f"Konversi gagal: {err}")

Metode Perbaikan dan Konfigurasi

1. Konfigurasi Runtime via sys.set_int_max_str_digits

Gunakan modul sys untuk menaikkan atau menonaktifkan limit pada script backend atau worker yang terisolasi dari input publik yang tidak terpercaya.

import sys

# Menaikkan batas ke 10000 digit
sys.set_int_max_str_digits(10000)

# Atau menonaktifkan limit sepenuhnya (gunakan 0)
sys.set_int_max_str_digits(0)

# Verifikasi konfigurasi
current_limit = sys.get_int_max_str_digits()
assert current_limit == 0

2. Konfigurasi Environtment Variable

Jika modifikasi kode sumber aplikasi tidak memungkinkan (misalnya saat menjalankan CLI pihak ketiga atau container Docker), atur env var PYTHONINTMAXSTRDIGITS pada level environment sistem:

# Menonaktifkan limit pada level container/shell
export PYTHONINTMAXSTRDIGITS=0
python3 worker_pipeline.py

# Menaikkan limit ke angka spesifik
export PYTHONINTMAXSTRDIGITS=8000
python3 worker_pipeline.py

Flag argumen CLI juga dapat digunakan secara langsung:

python3 -X int_max_str_digits=8000 worker_pipeline.py

Trade-off Keamanan vs Kebutuhan Throughput

Membuka limit menjadi 0 secara global pada service publik (misal: API gateway, handler Flask/FastAPI) sangat tidak disarankan karena mengembalikan kerentanan DoS CPU. Evaluasi arsitektur berdasarkan klasifikasi trust boundary:

  • Public-Facing API: Pertahankan limit default 4300 digit. Tolak payload yang melanggar spesifikasi bisnis pada ingress/middleware.
  • Internal Data Pipeline & Batch Worker: Jika sistem membaca data batch tepercaya dari data warehouse atau komputasi kriptografi, isolasi proses worker dan naikkan limit sesuai batas ekspektasi throughput.

Alternatif: Format Parsing dan Representasi Binary

Daripada melonggarkan keamanan sistem secara menyeluruh, pertahankan limit CPython dan gunakan alternatif serialisasi data berikut untuk integer berukuran masif:

Format Hexadesimal atau Base64

# Encode ke hex string (O(N) - aman dari limit desimal)
big_int = 10**5000
hex_representation = hex(big_int)

# Parsing kembali string hexadesimal ke integer tanpa error
parsed_int = int(hex_representation, 16)
assert parsed_int == big_int

Representasi Raw Bytes

# Serialisasi integer arbitrary precision langsung ke binary
raw_bytes = big_int.to_bytes((big_int.bit_length() + 7) // 8, byteorder="big")

# Deserialisasi kembali ke integer secara instan
restored_int = int.from_bytes(raw_bytes, byteorder="big")
assert restored_int == big_int

Penggunaan format binary atau encoding hexadesimal mengeliminasi overhead algoritma konversi desimal, menjaga performa serialisasi, dan memastikan backend tetap kebal terhadap eksploitasi CPU DoS.