34 lines
1.6 KiB
C#
34 lines
1.6 KiB
C#
using System.IO.Compression;
|
|
|
|
namespace Shared.Excel;
|
|
|
|
public sealed class XlsxSafetyInspector
|
|
{
|
|
public const long DefaultMaxUncompressedBytes = 250L * 1024 * 1024;
|
|
public const int DefaultMaxEntries = 5_000;
|
|
|
|
public void EnsureSafe(byte[] bytes, long maxUncompressedBytes = DefaultMaxUncompressedBytes, int maxEntries = DefaultMaxEntries)
|
|
{
|
|
using var stream = new MemoryStream(bytes, writable: false);
|
|
using var archive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false);
|
|
if (archive.Entries.Count == 0 || archive.Entries.Count > maxEntries)
|
|
throw new InvalidDataException("Excel 파일 내부 구조가 비정상적입니다.");
|
|
|
|
if (!archive.Entries.Any(x => x.FullName.Equals("[Content_Types].xml", StringComparison.OrdinalIgnoreCase)) ||
|
|
!archive.Entries.Any(x => x.FullName.Equals("xl/workbook.xml", StringComparison.OrdinalIgnoreCase)))
|
|
throw new InvalidDataException("유효한 .xlsx 통합문서가 아닙니다.");
|
|
|
|
long total = 0;
|
|
foreach (var entry in archive.Entries)
|
|
{
|
|
total = checked(total + entry.Length);
|
|
if (total > maxUncompressedBytes)
|
|
throw new InvalidDataException("압축 해제된 Excel 데이터가 허용 크기를 초과합니다.");
|
|
|
|
// A very small compressed entry expanding to an extreme size is suspicious.
|
|
if (entry.CompressedLength > 0 && entry.Length > 10L * 1024 * 1024 && entry.Length / entry.CompressedLength > 200)
|
|
throw new InvalidDataException("비정상적인 압축률의 Excel 항목이 감지되었습니다.");
|
|
}
|
|
}
|
|
}
|