Django MultiPartParser base64 DoS 취약점과 CVE-2026-33033 대응
Django MultiPartParser의 base64 공백 처리 DoS 취약점 CVE-2026-33033 원인, 패치 버전과 다계층 방어 방법을 정리한다.
2026-08-14 · 최초 발행 2026-04-17
2026년 4월 7일 Django 프로젝트는 MultiPartParser의 base64 디코딩 처리에서 CPU를 소진시킬 수 있는 서비스 거부 취약점 CVE-2026-33033을 수정했다. 대량의 공백을 섞은 base64 multipart 요청만으로 인증 전 단계의 파서를 오래 붙잡아 둘 수 있는 알고리즘 복잡도 공격이다.
영향 범위와 패치 대상
CVE-2026-33033의 영향 범위는 Django 6.06.0.3, 5.25.2.12, 4.2~4.2.29다. Django 보안 정책상 심각도는 Moderate이며, 네트워크를 통해 원격으로 공격할 수 있고 인증도 필요하지 않다. 공격 복잡도는 Low로 분류됐다.
패치는 Django 6.0.4, 5.2.13, 4.2.30에 포함됐다. 취약점은 Seokchan Yoon이 보고하고 패치를 제공했다.
공백 처리 루프가 만드는 CPU 소모
Django의 MultiPartParser는 파일 업로드 multipart 요청에 Content-Transfer-Encoding: base64 헤더가 있으면 base64 디코딩을 수행한다. RFC 4648은 디코딩 과정에서 공백 문자를 무시하도록 규정한다. 기존 파서는 공백을 제거한 뒤 4바이트 정렬을 맞추는 방식으로 이를 처리했다.
문제는 청크에서 공백을 제외한 바이트 수가 4의 배수가 아닐 때 발생한다. 파서는 field_stream.read(1-3)로 추가 바이트를 읽으려 했고, 읽은 값이 공백이면 버린 뒤 다시 읽었다. 이 과정에서 매번 내부 버퍼 전체를 복사하고 unget()으로 나머지를 되돌렸다.
공백 바이트 하나마다 크기 n의 내부 버퍼 복사가 일어나고, 공백이 m개면 O(m * n)의 이차 시간 복잡도로 이어진다. 공백 위주의 2.5MB 페이로드는 정상 업로드보다 수백 배의 CPU 시간을 소비할 수 있었다.
인증 전 업로드 경로가 공격 표면이 된다
공격 요청은 Content-Type: multipart/form-data 파일 업로드 형식을 사용하며, Content-Transfer-Encoding: base64 헤더를 포함한다. 소량의 유효 base64 문자 사이에 스페이스, 탭, 줄바꿈 같은 공백을 대량으로 넣고, 각 청크의 non-whitespace 바이트 수가 4의 배수가 되지 않도록 배치하는 방식이다.
파일 업로드를 수락하는 Django 엔드포인트라면 로그인 없는 경로도 대상이 된다. 파서가 인증과 인가보다 먼저 요청을 처리하기 때문이다.
패치가 바꾼 읽기 방식
수정 전에는 정렬 루프가 field_stream.read(1-3)로 바이트 단위 읽기를 수행했다. 호출마다 전체 내부 버퍼를 복사하고 unget()을 수행하므로 공백이 연속되면 공백 수만큼 루프가 반복됐다.
패치에서는 정렬 루프가 self._chunk_size 단위로 읽는다. 공백을 제거한 조각은 리스트에 쌓고 마지막에 한 번만 join()한다. 그 결과 버퍼 복사 횟수는 O(m)에서 O(m / chunk_size)로 크게 줄었다.
| Django 브랜치 | 패치 버전 | 커밋 해시 |
|---|---|---|
| main | - | 7e9885f |
| 6.0.x | 6.0.4 | 0910af6 |
| 5.2.x | 5.2.13 | 0b46789 |
| 4.2.x | 4.2.30 | f13c20f |
같은 릴리스에 포함된 보안 수정
2026년 4월 7일 릴리스에는 CVE-2026-33033 외에도 네 건의 보안 취약점이 함께 수정됐다.
| CVE | 설명 | 심각도 |
|---|---|---|
| CVE-2026-3902 | ASGI 요청에서 언더스코어/하이픈 혼동을 통한 헤더 스푸핑 | Low |
| CVE-2026-4277 | GenericInlineModelAdmin 권한 우회 | Low |
| CVE-2026-4292 | ModelAdmin.list_editable 권한 남용 | Low |
| CVE-2026-33033 | MultiPartParser base64 DoS | Moderate |
| CVE-2026-33034 | ASGI 요청 Content-Length 우회로 메모리 업로드 제한 무력화 | Low |
CVE-2026-33034는 ASGI 요청에서 Content-Length 헤더를 생략하거나 실제보다 작게 지정해 DATA_UPLOAD_MAX_MEMORY_SIZE 제한을 우회하고, 무제한 요청 본문을 메모리에 적재할 수 있는 문제다. 이번 릴리스에서는 CVE-2026-33033과 CVE-2026-33034, 두 DoS 취약점이 함께 패치됐다.
multipart 파서에서 반복되는 DoS 패턴
Django 발표 직후인 2026년 4월 15일에는 FastAPI와 Starlette가 사용하는 python-multipart에서도 CVE-2026-40347이 공개됐다. 이 취약점은 첫 번째 boundary 전의 CR/LF 바이트를 바이트 단위로 스캔하는 비효율적 프리앰블 파싱, 닫는 boundary 뒤 데이터를 즉시 폐기하지 않고 계속 처리하는 비효율적 에필로그 파싱과 관련된다.
두 사례는 인증 없이 네트워크를 통해 공격할 수 있고 알고리즘 복잡도를 악용한다는 점에서 닮아 있다. 웹 프레임워크 파서에서는 다음 패턴을 점검할 필요가 있다.
- 청크나 배치 대신 1바이트씩 읽는 처리
- 입력 크기에 비례해 내부 버퍼를 반복 복사하는 이차 복잡도 루프
- 악성 패턴으로 정규식 엔진의 역추적을 유발하는 ReDoS, 예를 들어 Parse Server CVE-2026-30925
- 인증과 인가보다 먼저 동작해 모든 요청에 노출되는 파서
요청이 파서에 닿기 전 제한하기
사전 인증 DoS는 한 계층의 설정만으로 다루기 어렵다. WAF와 리버스 프록시에서 요청을 제한하고, 프레임워크와 애플리케이션에서 업로드 경로를 좁혀야 한다.
리버스 프록시에서는 Nginx의 client_max_body_size, client_body_timeout 등을 사용해 요청 크기와 전송 시간을 제한할 수 있다. multipart 요청의 Content-Transfer-Encoding 헤더와 비정상 인코딩 패턴은 WAF 검사 대상이 되며, IP와 엔드포인트 기준 Rate Limiting도 함께 적용한다. Cloudflare, AWS Shield 같은 L7 DDoS 방어 서비스도 방어 계층에 포함할 수 있다.
Django에서는 DATA_UPLOAD_MAX_MEMORY_SIZE로 메모리 기반 업로드 크기를 제한할 수 있으며 기본값은 2.5MB다. DATA_UPLOAD_MAX_NUMBER_FILES는 단일 요청의 최대 파일 수를, FILE_UPLOAD_MAX_MEMORY_SIZE는 파일 업로드 시 메모리 사용량을 제한한다. 요청 처리 시간의 상한을 두는 파싱 타임아웃과 신속한 프레임워크 보안 패치 적용도 필요하다.
애플리케이션에서는 파일 업로드를 받는 모든 뷰에 인증 데코레이터를 적용하고, 미들웨어에서 허용된 Content-Type만 수락하도록 구성한다. 파일 확장자, MIME 타입, 매직 바이트를 함께 검증하며 CPU 사용률과 요청 처리 시간 급증에 대한 자동 알림을 둔다.
우선 확인할 조치
python -c "import django; print(django.VERSION)"로 현재 Django 버전을 확인한다.- 사용 중인 브랜치에 맞춰 Django 6.0.4, 5.2.13, 4.2.30으로 업데이트한다.
- 리버스 프록시의 multipart 요청 크기와 타임아웃 설정을 점검한다.
- 인증 없이 접근 가능한 파일 업로드 엔드포인트를 식별하고 보호한다.
- FastAPI나 Starlette를 사용한다면
python-multipart라이브러리를 0.0.26 이상으로 업데이트한다.
Sources
- Django security releases issued: 6.0.4, 5.2.13, and 4.2.30
- Fixed CVE-2026-33033 -- Mitigated potential DoS in MultiPartParser (main)
- Django 6.0.4 release notes
- Django 4.2.30 release notes
- oss-security: Django CVE-2026-3902, CVE-2026-4277, CVE-2026-4292, CVE-2026-33033, and CVE-2026-33034
- Archive of security issues - Django documentation
- python-multipart CVE-2026-40347 DoS vulnerability
- Denial of Service - OWASP Foundation