Password-protected ZIP with Python: pyzipper, AES-256 and what NOT to use
The Python standard library ships zipfile, which technically supports encryption — but only the legacy ZipCrypto algorithm, broken since the early 2000s and crackable in seconds with off-the-shelf tools. For real protection you need AES-256, and that means pyzipper. This guide covers how to use it correctly, compares it to alternatives, and shows the production patterns that actually keep your data safe.
Why not the standard library zipfile?
# DON'T DO THIS — broken encryption
import zipfile
with zipfile.ZipFile('insecure.zip', 'w') as zf:
zf.write('secret.txt')
zf.setpassword(b'hunter2')
Three problems:
- Python's
zipfile.setpassword()only applies to reading encrypted files, not creating them. To create encrypted ZIPs with stdlib you literally cannot. - Even when you generate ZipCrypto-encrypted files with another tool and read them with
zipfile, the encryption itself is broken — known-plaintext attacks recover the password in milliseconds. - The "safe" assumption that "it's encrypted, so it's fine" is exactly what attackers exploit. ZIP files marked encrypted with ZipCrypto are roughly equivalent to having the password in plaintext on the file.
Install pyzipper
pip install pyzipper
# Or with uv (faster):
uv pip install pyzipper
pyzipper is a drop-in replacement for zipfile that adds AES-128, AES-192 and AES-256 support via the WinZip AES specification (also supported by 7-Zip, WinRAR and modern Windows Explorer).
Create an AES-256 encrypted ZIP
import pyzipper
with pyzipper.AESZipFile(
'secure.zip',
mode='w',
compression=pyzipper.ZIP_LZMA, # better compression than DEFLATE
encryption=pyzipper.WZ_AES, # WinZip AES spec
) as zf:
zf.setpassword(b'a-strong-passphrase-not-this-one')
zf.setencryption(pyzipper.WZ_AES, nbits=256)
zf.write('confidential.pdf')
zf.write('client-data.csv')
Key settings:
compression=pyzipper.ZIP_LZMA— better than DEFLATE for text/code; for already-compressed files (JPG, MP4) useZIP_STOREDto skip compression entirelyencryption=pyzipper.WZ_AES— WinZip AES, the modern standardnbits=256— AES-256 (also accepts 128 or 192). Use 256 unless you have a specific compatibility reason
Read an encrypted ZIP
with pyzipper.AESZipFile('secure.zip') as zf:
zf.setpassword(b'a-strong-passphrase-not-this-one')
# Extract everything to current dir
zf.extractall()
# Or read a single file into memory
data = zf.read('confidential.pdf')
If the password is wrong, pyzipper raises RuntimeError: Bad password for file .... Wrap in try/except for production code.
Add files from memory (no disk I/O)
When you generate content in Python and want to encrypt it without touching disk:
import io
import pyzipper
buffer = io.BytesIO()
with pyzipper.AESZipFile(
buffer, 'w',
compression=pyzipper.ZIP_DEFLATED,
encryption=pyzipper.WZ_AES,
) as zf:
zf.setpassword(b'strong-password')
zf.setencryption(pyzipper.WZ_AES, nbits=256)
zf.writestr('report.json', '{"key": "value"}')
zf.writestr('summary.txt', 'Report generated 2026-05-07')
# buffer.getvalue() is now an encrypted ZIP byte string ready to:
# - upload to S3
# - email as attachment
# - save to disk
# - return from a Flask/FastAPI endpoint
Production pattern — encrypt a folder for client delivery
import os
import pyzipper
from pathlib import Path
def encrypt_folder(folder: Path, output: Path, password: bytes) -> None:
with pyzipper.AESZipFile(
output, 'w',
compression=pyzipper.ZIP_LZMA,
encryption=pyzipper.WZ_AES,
) as zf:
zf.setpassword(password)
zf.setencryption(pyzipper.WZ_AES, nbits=256)
for path in folder.rglob('*'):
if path.is_file():
# Preserve relative path inside the zip
arcname = path.relative_to(folder)
zf.write(path, arcname)
encrypt_folder(
Path('./client-deliverables'),
Path('client-2026-05-deliverables.zip'),
os.environ['CLIENT_ZIP_PASSWORD'].encode(),
)
Two production tips here:
- Never hardcode passwords — read from environment variables, secret managers (Vault, AWS Secrets Manager, GCP Secret Manager), or stdin
- Use Path.rglob and relative_to to preserve folder structure inside the ZIP
Compatibility — who can open AES-256 ZIPs?
| Tool | Reads AES-256 |
|---|---|
| 7-Zip (Windows/Linux/macOS) | ✅ |
| WinRAR | ✅ |
| Windows 10/11 Explorer (built-in) | ✅ since 2017 |
| macOS Archive Utility | ❌ — needs Keka or similar |
| Linux unzip (Info-ZIP) | ❌ — needs 7z x instead |
| iOS Files app | ❌ |
| Android stock file managers | depends on vendor |
| pyzipper / zipfile + pyzipper | ✅ |
If your recipient is on macOS or mobile and doesn't have a third-party archive tool, send them 7z encrypted instead (pip install py7zr) — every modern OS has at least one app that handles 7z natively, and the encryption story is the same AES-256.
When NOT to use ZIP at all
- At-rest disk encryption needed — use full-disk encryption (BitLocker, LUKS, FileVault). ZIP is per-file and leaves filenames visible in the central directory.
- Long-term archival — ZIP central directory is fragile; a single byte flip can corrupt the entire archive's index. Consider tar+gzip+age (modern encryption) for ≥10-year storage.
- Sending to recipients with unknown tools — encrypted PDF (with
qpdf --encrypt) or password-protected document formats (DOCX, PPTX) are more universally readable.
Brute-force resistance — what password strength matters
AES-256 is mathematically secure for the foreseeable future. The weak link is always the password. As of 2026:
- 8-character random ASCII: ~52 bits, broken in days on a single GPU
- 12-character random ASCII: ~78 bits, broken in months on a botnet
- 16-character random ASCII: ~104 bits, beyond reach
- 4-word Diceware passphrase: ~52 bits — same weakness as 8 chars
- 6-word Diceware passphrase: ~78 bits, marginal
- 8-word Diceware passphrase: ~104 bits, secure
Use a password manager to generate and store. The 5-second annoyance per archive is dwarfed by the cost of the breach.
Related conversions
If you're working with archives and security:
- RAR to ZIP — convert proprietary RAR to open ZIP
- 7z to ZIP — when recipient cannot open 7z
- TAR to ZIP — Linux archives to Windows-friendly format
- ZIP to RAR — for environments that prefer RAR
- GZ to ZIP — single-file gzip to multi-file ZIP
- ISO to ZIP — extract ISO contents into a ZIP archive