엑셀 업로드 중 발생한 Zip Bomb 에러 파헤치기! 🥊
Zip Bomb 에러 소개 및 해결 방법 공유
Zip Bomb 문제 해결 여정에 오신 것을 환영합니다!
안녕하세요. 딜리버리프로덕트개발팀 최지수입니다. 제가 유지 보수하는 시스템에는 고객사가 엑셀 파일을 업로드하여 배송 요청을 할 수 있는 기능이 있습니다. 최근 이 기능에서 예상치 못한 에러가 발생했는데, 바로 "Zip Bomb" 에러였습니다!
이번 글에서는 이 문제를 분석하고 해결하는 과정을 통해 배운 내용을 공유하고자 합니다.
Zip Bomb 에러의 진짜 정체! 😮
Zip Bomb? 그게 뭐죠?
Zip Bomb은 작지만, 치명적인 공격 방식입니다. 압축 파일의 크기는 작아 보이지만, 이를 풀면 엄청난 양의 데이터가 생성되어 시스템의 메모리나 디스크 공간을 빠르게 소모하게 합니다. 이 때문에 서버나 애플리케이션이 멈추거나 느려질 수 있습니다. 특히 대량의 파일을 처리해야 하는 서버나 클라우드 환경에서 Zip Bomb은 전체 시스템을 마비시킬 위험이 있습니다.
가장 무서운 점은 이러한 문제가 발생할지 파일을 열기 전까지는 예측하기 어렵다는 것입니다. 압축 파일이 겉보기에는 안전해 보여도, 해제하는 순간 문제가 터질 수 있는 것이죠. 그러므로 Zip Bomb을 방어하는 것이 매우 중요합니다.
왜 이런 문제가 발생할까요?
이 문제는 파일 압축 방식의 특성 때문입니다. 압축 파일은 중첩된 데이터 구조가 숨겨져 있어, 압축을 해제하는 순간 시스템의 메모리와 디스크 공간을 급격히 소모하게 할 수 있습니다. 예를 들어, 몇 KB 크기의 작은 압축 파일이지만, 해제 후에는 수 GB에 달하는 데이터로 확장될 수 있죠.
이러한 문제는 특히 중첩 압축 방식을 사용할 때 심화됩니다. 한 번 압축된 데이터를 다시 압축하거나, 여러 단계에 걸쳐 반복적으로 압축하면 파일 크기는 매우 작아지지만, 그만큼 해제 과정에서 시스템에 큰 부담을 줄 수 있습니다. 이를 방지하기 위해 압축 비율을 점검하거나 압축된 레벨을 제한하는 것이 중요합니다.
엑셀 파일과의 연결 고리는?
엑셀 파일도 사실 압축 파일입니다! 엑셀 파일(.xlsx)은 여러 XML 파일과 폴더로 구성된 압축 구조로 되어 있습니다. 이를 압축 해제하면 xl
, docProps
, rels
같은 폴더와 다양한 파일들이 나타납니다. 이 파일들은 각 시트, 셀 서식, 스타일 등 엑셀의 복잡한 데이터와 구조를 관리하는 역할을 합니다. 아래 사진은 실제로 엑셀 파일을 압축 해제했을 때 볼 수 있는 파일들입니다.
참고로 압축 해제는 unzip 엑셀파일
명령어로 간단히 실행할 수 있습니다.
Java에서 엑셀 파일을 처리할 때 주로 사용하는 라이브러리는 Apache POI입니다. Apache POI는 엑셀 파일의 ZIP 압축을 풀고 XML 데이터를 파싱하는데, 이 과정에서 압축률이 비정상적으로 높은 파일을 Zip Bomb으로 감지할 수 있습니다.
아래 예시 코드처럼 XSSFWorkbook
객체를 생성하는 단계에서 엑셀 파일의 ZIP 압축이 해제됩니다. Apache POI는 이 단계에서 압축률을 검사해 비정상적인 경우 Zip bomb detected
오류를 발생시킬 수 있습니다.
FileInputStream fileInputStream = new FileInputStream("sample.xlsx");
XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
도대체 뭐가 문제였을까요? 🔍
원인을 파악하기 위해 먼저 에러 로그를 확인했습니다. 로그에는 파일 크기, 시스템에서 설정된 최소 압축 비율, 그리고 문제를 일으킨 파일에 대한 정보가 담겨 있었습니다. 이를 하나씩 살펴보겠습니다.
Zip bomb detected! The file would exceed the max. ratio of compressed file size to the size of the expanded data.
This may indicate that the file is used to inflate memory usage and thus could pose a security risk.
You can adjust this limit via ZipSecureFile.setMinInflateRatio() if you need to work with files which exceed this limit.
Uncompressed size: 406416, Raw/compressed size: 4059, ratio: 0.009987
Limits: MIN_INFLATE_RATIO: 0.010000, Entry: xl/styles.xml
로그에 나와 있는 내용
Zip bomb detected!
시스템이 이 파일을 "Zip Bomb"으로 인식했다는 의미입니다. 간단히 말해, 압축된 파일 크기와 압축 해제 후 파일 크기의 비율이 지나치게 높아서 시스템 자원을 과도하게 사용할 위험이 있다는 경고입니다.
Ratio exceeded
압축 파일의 크기와 해제된 데이터 크기 간의 비율이 설정된 한계를 초과했음을 나타냅니다. 이로 인해 시스템 자원이 크게 소모될 수 있고, 심하면 메모리 부족 문제를 유발할 수도 있습니다.
Security risk
이 파일이 시스템에 보안 위협을 가할 가능성이 있다는 경고입니다. 메모리 사용이 비정상적으로 급증하면서 시스템이 불안정해질 수 있다는 뜻이죠. 예상보다 훨씬 큰 데이터를 해제할 때 이러한 위험이 발생합니다.
ZipSecureFile.setMinInflateRatio()
이 경고를 피하려면 ZipSecureFile.setMinInflateRatio()
메서드를 사용해 최소 압축 해제 비율을 조정할 수 있습니다. 설정된 비율을 초과하는 파일들만 경고를 발생시키도록 설정할 수 있습니다.
파일 크기와 관련된 정보
Uncompressed size (406,416 bytes)
이 값은 압축 해제 후의 파일 크기로 406,416 bytes(약 406KB)입니다. 이 파일은 압축을 해제한 후 시스템이 처리해야 하는 실제 데이터의 크기를 나타냅니다.
Raw/compressed size (4,059 bytes)
압축된 파일의 크기입니다. 이 파일은 4,059 bytes(약 4KB)로 매우 작습니다. 이는 파일이 강력하게 압축되어 있다는 것을 의미합니다.
Ratio (0.009987)
압축 비율을 보여주는 값으로, 압축된 크기와 해제된 크기의 비율을 나타냅니다. 0.009987은 약 100배의 차이가 난다는 뜻입니다. 아주 작은 파일이 압축 해제 후 매우 큰 데이터를 내포하고 있다는 것이 문제입니다.
시스템에 설정된 최소 압축 비율
MIN_INFLATE_RATIO: 0.010000
시스템에서 허용하는 최소 압축 비율은 0.01입니다. 즉, 파일이 100배 이상 압축된 경우 위험 요소로 인식됩니다. 문제의 파일은 이 비율을 약간 초과해 경고가 발생한 것입니다.
문제를 일으킨 파일
Entry: xl/styles.xml
문제의 파일은 엑셀 파일 내의 styles.xml
로, 이 파일은 엑셀 셀 서식, 즉 텍스트나 셀의 스타일을 정의하는 파일입니다.
문제의 원인을 정확히 파악하기 위해, 엑셀 파일을 직접 압축 해제하고 styles.xml
파일을 확인해 보았습니다. 아래 코드는 문제의 styles.xml
파일에서 발견된 코드의 예시로, 여러 셀 스타일이 반복적으로 정의되어 있었습니다.
일반적으로 사용되는 압축 알고리즘(예: ZIP, gzip)은 데이터를 압축할 때 반복되는 문자열이나 패턴을 감지하여 더 짧은 코드로 대체합니다. 특히 같은 코드가 많이 반복될 경우, 더 적은 비트로 데이터를 표현할 수 있어 압축률이 극대화됩니다.
이처럼 같은 데이터의 과도한 반복은 압축률을 지나치게 높일 수 있으며, 이번 문제의 원인은 바로 styles.xml 파일 내 과도한 스타일 정의 반복이었습니다. 이 반복이 압축률을 지나치게 높여 시스템이 해당 파일을 Zip Bomb으로 인식하게 한 주된 이유였습니다.
<dxf>
<font>
<color rgb="FF9C0006"/>
</font>
<fill>
<patternFill>
<bgColor rgb="FFFFC7CE"/>
</patternFill>
</fill>
</dxf>
<dxf>
<font>
<color rgb="FF9C0006"/>
</font>
<fill>
<patternFill>
<bgColor rgb="FFFFC7CE"/>
</patternFill>
</fill>
</dxf>
에러와의 한판 승부! 💪
Zip Bomb을 막는 방법들, 어떤 것들이 있을까요?
Zip Bomb을 막는 방법은 여러 가지가 있습니다. 이 문제를 해결하기 위해 압축 파일의 크기와 압축률을 검사하고, 이를 초과하는 파일을 사전에 차단하는 방식이 대표적입니다. 구체적으로 어떤 방법이 있는지 살펴보겠습니다.
압축 해제 비율 조정
압축 파일의 최소 해제 비율을 설정하면, 비정상적으로 높은 압축 비율을 가진 파일을 걸러낼 수 있습니다. 이를 통해 Zip Bomb을 의심할 만한 파일을 미리 차단할 수 있습니다.
ZipSecureFile.setMinInflateRatio(0.01); // 최소 압축 해제 비율 설정
파일 크기 제한
압축 파일의 크기나 압축 해제 후 파일 크기에 제한을 두는 것도 좋은 방법입니다. 너무 큰 파일은 Zip Bomb일 가능성이 있으므로, 시스템에서 이를 미리 감지하고 차단할 수 있습니다. 압축 파일을 실제로 해제하기 전에 파일 내의 각 항목(파일)의 압축 해제 크기를 확인하는 것입니다. 많은 압축 라이브러리는 이 정보를 제공하므로, 이를 통해 사전에 비교할 수 있습니다.
while ((entry = zipEntery.getNextEntry()) != null) {
uncompressedSize += entry.getSize();
zis.closeEntry();
}
// 총 압축 해제 크기를 체크
if (uncompressedSize > MAX_ALLOWED_SIZE) {
throw new SecurityException("File too large: " + uncompressedSize + " bytes");
}
압축 파일의 계층 제한
Zip Bomb은 여러 레벨의 압축을 중첩해 파일을 더욱 복잡하게 만듭니다. 압축 파일의 중첩된 레벨이 일정 수준을 초과하지 않도록 제한할 수 있습니다.
if (zipEntry.getDepth() > MAX_NESTED_LEVEL) {
throw new SecurityException("Too many nested levels in the ZIP file");
}
저희는 어떻게 해결했을까요?
저희 서비스는 계약된 사용자만 시스템에 접근할 수 있어 악의적인 공격 가능성이 낮다고 판단했습니다. 이에 따라, 1차 조치로 최소 압축 비율 설정 값을 낮추어 압축 비율 검사를 완화했습니다. 앞으로 더 근본적인 보안 대책을 마련할 계획입니다. 시스템의 안정성을 유지하고 보안을 강화하기 위해 지속해서 개선해 나갈 것입니다.
마무리 🖋️
문제의 원인을 파악하고 해결하는 과정에서 로그가 담고 있는 귀중한 정보의 중요성을 다시금 깨닫게 되었습니다. 각 로그 메시지는 단순한 오류 코드 그 이상으로, 시스템의 건강을 유지하는 데 필요한 중요한 단서들을 제공하는 것 같습니다.
이 경험을 통해 문제를 단순히 해결하는 것을 넘어, 미래의 유사한 상황에 대비할 수 있는 지식을 쌓을 수 있었습니다.🤓