BabelDOC가 PDF 처리 과정을 Rust로 옮기고 문서를 읽을 때 쓰는 자원에 기본 상한을 둔 새 버전을 공개했다. 외부에서 받은 PDF를 번역하는 서비스는 작은 악성 파일이 메모리를 대량으로 소모해 작업을 멈추게 하는 위험을 줄일 수 있다.

1. PDF를 읽고 다시 쓰는 과정을 Rust로 옮겼다

이번 버전은 PDF 파싱뿐 아니라 스캔 문서 판별, 글과 그림 배치, 문단·스타일 처리, 번역 조정, 페이지 요소를 인쇄 형태로 배열하는 조판과 PDF 쓰기까지 Rust에서 수행한다. 배치 모델, 문서 요소의 위치를 정하는 모델과 번역 서비스 연동은 파이썬에 남아 있고, 파이썬은 계속 사용 진입점 역할을 한다.

프로젝트 발표 기준으로 PDF를 읽는 속도는 0.6.4보다 빨라지고 메모리 사용량은 줄었다. 기본 처리 경로의 문서 출력은 이전 버전과 같게 유지하는 것이 목표이며, 의도한 차이는 워터마크 변경과 새 자원 상한이다.

2. 작은 입력이 메모리를 소진하는 경로를 막는다

0.7.0은 입력 PDF를 읽는 동안 자원이 지나치게 늘어나는 것을 막기 위한 상한을 기본으로 켠다. 상한 초과 시 처리를 멈추고 초과한 제한의 이름과 값, 발생 위치를 담은 예외를 낸다. 제한은 한꺼번에 끌 수 있지만, 이 경우 경고가 기록된다.

첫 번째 보안 권고는 압축된 PDF 데이터를 풀 때 출력 크기를 제한하지 않아 메모리를 소진하는 문제다. 압축 파일을 열자 작은 꾸러미가 거대한 짐으로 불어나는 셈이다.

권고에 실린 재현 기록 기준으로 Python 3.11 슬림 컨테이너의 BabelDOC 0.6.4는 1,043,644바이트를 1,073,741,824바이트로 풀었다. 압축 해제에는 2.70초가 걸렸고, 프로세스 최대 메모리는 1,068,544KiB에서 2,116,024KiB로 늘었다.

재현의 핵심 코드 일부는 다음과 같다.

stream = PDFStream(attrs={"Filter": LIT("FlateDecode")}, rawdata=payload)

mem_before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print(f"\nProcess RSS before decode(): {mem_before} KiB")

start = time.time()
stream.decode()  # <-- the real, unmodified vulnerable call: zlib.decompress(data), no max size
elapsed = time.time() - start

두 번째 권고는 글꼴의 문자 코드를 실제 문자에 연결하는 ToUnicode 매핑 문제다. 문자 코드 범위를 하나씩 펼쳐 저장하면서 자원을 소모했다. BabelDOC 0.6.4에서 기존 파이썬 파서와 후속 파이썬 파서에 같은 입력을 전달한 재현 기록은 다음과 같다.

표: ToUnicode 범위 전개 재현 결과

조건: BabelDOC 0.6.4, Docker 안의 Python, 네트워크 차단, 파서 입력 바이트 직접 호출(2026년 10월 16일 실행 결과)

단위: 시간은 초, 항목 수는 개

작은 입력이 메모리를 소진하는 경로를 막는다
파서·범위 끝값시간항목 수
기존 파이썬 파서·000000FF0.0004—
기존 파이썬 파서·000FFFFF0.41—
기존 파이썬 파서·00FFFFFF6.72—
후속 파이썬 파서·000000FF0.0256
후속 파이썬 파서·000FFFFF0.541048576
후속 파이썬 파서·00FFFFFF18.4416777216

표의 범위는 모두 00000000에서 시작한다. 같은 파서에서 범위를 넓히자 처리 시간이 늘었다. 별도로 00000000~FFFFFFFF 범위를 메모리 1GiB 제한 아래 펼친 시험은 종료 코드 137로 강제 종료됐다. 새 Rust 파서는 범위를 개별 항목이 아닌 구간으로 저장하고, 보존하는 매핑 데이터에도 기본 상한을 적용한다.

3. 보안 개선의 대가는 제한 초과 문서의 실패 처리다

두 권고의 취약 버전은 모두 0.6.4 이하로 표기됐으며, 수정은 0.7.0에 포함됐다. 2026년 11월 7일 공개된 이번 버전은 자원 증폭을 일으키는, 입력 처리 중 자원이 크게 늘어나는 파이썬 파서 구현을 Rust 구현으로 대체했다. 유지관리자는 보안 수정이 최신 릴리스에만 제공되고 이전 버전에 대한 공식 역이식, 수정 사항을 이전 버전에 적용하는 작업은 하지 않는다고 밝혔다.

자원 상한은 악성 입력의 과도한 메모리 사용 위험을 낮추는 대신, 정상 문서라도 상한을 넘으면 번역을 거부한다. 서비스 운영자는 실패 이유를 사용자에게 알리는 절차를 준비할 필요가 있다. 꼭 처리해야 하는 문서는 상한을 끄고 외부 제한을 적용하는 방법이 안내됐다.

재현 수치는 파서를 직접 호출한 결과이며 전체 번역이나 새 Rust 구현의 성능 측정값은 아니다. 유지관리자는 신뢰할 수 없는 PDF를 처리할 때 번역 프로세스의 시간 제한과 컨테이너 메모리 제한도 함께 적용하도록 권고했다.

4. 사전 빌드 패키지를 쓰고 자체 문서로 결과를 확인한다

사전 빌드 패키지, 미리 만들어 바로 설치하는 패키지는 리눅스의 x86-64·ARM64, 윈도 x86-64, 맥의 인텔·애플 실리콘 등 다섯 플랫폼에 제공된다. 지원하는 파이썬 버전은 3.10~3.13이다. 다른 플랫폼에서 소스 배포판, 소스 코드와 빌드 설정을 묶은 배포 파일을 설치하거나 저장소에서 직접 빌드할 때는 Rust 도구 모음, 컴파일과 빌드에 필요한 도구 묶음이 필요하다.

공개 진입점은 유지되지만, 삭제된 내부 파이썬 모듈을 직접 불러오는 외부 코드는 수정해야 한다. 유지관리자는 운영 환경을 업그레이드하기 전에 자체 문서로 결과를 확인하도록 안내했다.

첫 페이지 워터마크는 더 이상 추가되지 않는다. 관련 설정은 계속 받되 출력 파일 이름과 개수만 바꾸므로, 결과물 확인 때 함께 살필 변화다.

0.7.0은 PDF 파서의 위험한 자원 증폭 경로를 제한하는 대신 일부 문서를 실패 처리할 수 있으므로, 업그레이드의 핵심은 제한 초과 작업을 운영에서 어떻게 다룰지 준비하는 데 있다.