Why File Size Checks Miss ZIP Corruption
Most online unzip tools verify file size and nothing else. But flipping a single byte leaves the file length unchanged. Here is why the ZIP specification stores a 32-bit CRC32 checksum and how in-browser validation catches damage before extraction.
ZIP Archive Toolkit
100% In-browser execution. Zero server uploads, instant results, free forever.
- 1. The Length Illusion: Why Size Checks Pass Corrupted Payloads
- 2. The ZIP CRC-32 Architecture: Polynomial Checksums Over Bytes
- 3. Automated Browser Proof: Flipping Exactly One Byte
- 4. Path Sanitization & Zip Slip Defense
- 5. What CRC32 Detects, and What It Cannot
- 6. Why a Size Check Misses Almost Everything
- 7. Frequently Asked Questions (FAQ)
The Length Illusion: Why Size Checks Pass Corrupted Payloads
When unpacking an archive, conventional client tools inspect the local file header or central directory record, decompress the payload using the browser's native DecompressionStream('deflate-raw'), and compare the resulting buffer length against the declared uncompressedSize.
If you compress a 429-byte text file, decompress it, and receive 429 bytes, standard logic assumes the job succeeded.
However, length measurement is fundamentally blind to byte alteration:
- A transmission glitch or bit-flip on disk corrupts data without changing the total byte count.
- If byte 42 of your binary payload flips from 0x4A to 0xB5, the file is still exactly 429 bytes long.
- Handing those corrupted bytes to the user risks silent data destruction—from corrupted SQLite database headers to malfunctioning binary executables.
A length check cannot detect corruption. Only an independent mathematical checksum of the original bytes can distinguish intact data from damaged storage.
The ZIP CRC-32 Architecture: Polynomial Checksums Over Bytes
The PKWARE ZIP specification mandates that every entry record its original uncompressed content as an IEEE 802.3 32-bit Cyclic Redundancy Check (crc32).
In lib/tools/archive/zip-reader.ts, our extraction pipeline executes a strict verification contract:
1. Header Parsing: The central directory provides the authoritative 32-bit CRC32 integer (entry.crc32).
2. Decompression: The compressed slice is piped through DecompressionStream('deflate-raw').
3. Buffer Check: The uncompressed length is validated against entry.uncompressedSize.
4. CRC-32 Recomputation: The inflated buffer is passed through the IEEE 802.3 CRC32 lookup table:
``typescript
const actual = crc32(data);
if (actual !== entry.crc32) {
throw new Error(
"${entry.path}" failed its checksum — the archive expects ${entry.crc32.toString(16).padStart(8, '0')} and the data gives ${actual.toString(16).padStart(8, '0')}. The file is damaged.
);
}
``
If even a single byte differs, the polynomial computation yields a completely divergent 32-bit integer, immediately halting extraction.
Automated Browser Proof: Flipping Exactly One Byte
We do not assume this check functions—we pin it with automated browser end-to-end testing in e2e/archive-toolkit.spec.ts.
In the test named *"refuses a damaged file instead of handing it over"*:
1. The test loads stored.zip from disk.
2. It locates the payload byte offset of readme.txt.
3. It performs a bitwise XOR flip on exactly one byte: bytes[dataStart] ^= 0xff.
4. The file length remains identical.
5. The browser loads the archive into /file/archive and clicks "Take out".
The result is deterministic: the UI immediately catches the mismatch, renders an alert reading failed its checksum, and displays zero download or save buttons. The user is protected from receiving damaged files.
Path Sanitization & Zip Slip Defense
Beyond checksum validation, local archive extraction must defend against Zip Slip directory traversal attacks.
A malicious archive can define an entry path such as ../../.ssh/authorized_keys or ../../../../etc/passwd. If extracted naively, it overwrites critical files on the operating system.
Our reader inspects every entry path before extraction:
- Flags root-relative paths (/foo, C:\foo).
- Rejects entries containing folder-traversal components (..).
- Refuses non-printable control characters that mask authentic paths.
All warnings are highlighted in the UI index before any file is saved.
What CRC32 Detects, and What It Cannot
CRC32 is a cyclic redundancy check, designed for accidental corruption on storage and transmission channels. Its guarantees are precise and worth knowing rather than assuming.
A 32-bit CRC with a well-chosen polynomial — ZIP uses 0xEDB88320 in its reflected form — reliably detects:
- any single-bit error;
- any two-bit error within a very large window;
- any odd number of bit errors;
- any burst error shorter than 33 bits, which is the common failure mode for damaged storage and dropped packets.
For random corruption beyond those classes, the probability of a collision is approximately 1 in 2^32, about one in 4.3 billion.
What it categorically does not provide is tamper detection. CRC32 is a linear function, which means an attacker who modifies file contents can compute exactly which further bits to change to restore the original checksum. That is not a weakness in the implementation; it is arithmetic. Anyone verifying authenticity rather than integrity needs a cryptographic hash such as SHA-256, or a signature.
The practical framing: CRC32 answers "did this arrive intact?" and never "did this come from who I think?"
Why a Size Check Misses Almost Everything
Comparing the uncompressed byte count against the expected figure is the check most extraction code performs, and it detects only one failure mode: truncation.
It cannot see a flipped bit, a swapped block, a corrupted byte in the middle of a stream, or a decompression that produced the right length from the wrong data. All of those produce a file of exactly the expected size that is nonetheless wrong.
This matters most for the formats where corruption is silent. A damaged JPEG usually looks visibly wrong. A damaged CSV, SQL dump, or configuration file frequently opens without complaint and is simply incorrect — which is discovered much later, after the data has been used.
The ZIP format already stores the correct answer: every entry carries a CRC32 computed at compression time, in both the local header and the central directory. Verifying costs one pass over the decompressed bytes, which for a browser-based extractor is work already being done as the data streams out of the inflater.
The reason many tools skip it is that failing loudly is inconvenient. It is nonetheless the right behaviour: a file that fails its checksum should be reported, not silently handed over, because the alternative is a user acting on data that is quietly wrong.
Frequently Asked Questions (FAQ)
Does verifying CRC32 slow down extraction in the browser?
No. Modern JavaScript engines execute table-driven CRC32 calculations at hundreds of megabytes per second in browser RAM, adding negligible sub-millisecond overhead to extraction.
What causes a ZIP CRC32 checksum mismatch?
Incomplete network downloads, bad sectors on physical storage drives, memory bit-flips, or truncated transfers during file writing.
Does OpenTools upload my ZIP archives to a remote server?
No. All central directory parsing, decompression, and CRC32 verification execute 100% locally in your browser memory via native Web Streams and JavaScript.
What does a CRC32 checksum actually detect?
It reliably detects any single-bit error, any odd number of bit errors, and any burst error shorter than 33 bits, which covers the common failure modes of damaged storage and dropped packets. For other random corruption the chance of a collision is about one in 4.3 billion. It cannot detect deliberate tampering, because CRC32 is linear and an attacker can compute compensating changes that restore the original value.
Is CRC32 good enough for security?
No. CRC32 is designed for accidental corruption, not authenticity. Because the function is linear, anyone modifying a file can calculate which additional bits to flip so the checksum still matches. Verifying that data came from a particular source requires a cryptographic hash such as SHA-256 or a digital signature.
Why is checking file size not enough to catch corruption?
Because size only detects truncation. A flipped bit, a swapped block or a corrupted byte in the middle of a stream all produce a file of exactly the expected length. This is most dangerous for formats that fail silently, such as CSV files, SQL dumps and configuration files, which open without complaint and are simply wrong.
Related Guides & Solutions
The macOS ZIP Bug Where Filename Flags Lie
Four Pure-TypeScript First Issues in OpenTools
We Tried to Make Our Own Site Leak Your File
Ready to use ZIP Archive Toolkit?
Execute this workflow privately on your device right now without creating an account or paying for cloud API credits.