Python’s popularity is also its biggest security risk. The language that powers data pipelines, web backends, and machine learning infrastructure is the same language where a single poisoned dependency can compromise an entire deployment pipeline. In 2026, Python security is not optional — it is table stakes.
Here are the seven practices that move the needle from vulnerable to hardened.
1. Pin your dependencies and lock your hashes
Every requirements.txt with unpinned versions is a supply chain attack waiting to happen. Use pip freeze with exact versions, and add hash checking:
pip install pip-tools
pip-compile --generate-hashes requirements.in
pip install --require-hashes -r requirements.txt
The extra five minutes this takes prevents an attacker from slipping a malicious version of a transitive dependency into your build.
2. Stop using pickle for anything that crosses a trust boundary
The pickle module can execute arbitrary code during deserialization. CVE-2026-34091, the “pickle-in-the-middle” vulnerability discovered in Vertex AI pipelines earlier this year, demonstrated exactly how dangerous it remains in production ML workflows.
Replace pickle with JSON for simple data, MessagePack for performance, or Apache Arrow for large numerical arrays. None of these are Turing-complete deserialization formats that can execute code.
3. Scan dependencies in CI, not just on your laptop
Add a dependency scanner to your CI pipeline. pip-audit integrates with GitHub Actions and GitLab CI and checks against the PyPA advisory database:
- name: Audit dependencies
run: pip-audit --require-hashes -r requirements.txt
Fail the build on any vulnerability with a CVSS score above 7.0. The false positive rate is low enough that ignoring the signal is not justified.
4. Use environment variables, never hardcoded secrets
Every secret committed to a repository is a secret leaked. Use python-dotenv for local development and a secrets manager — AWS Secrets Manager, HashiCorp Vault, or GCP Secret Manager — for production. Validate at startup that every required secret is present and non-empty, so a missing secret fails fast instead of silently degrading.
5. Set restrictive file permissions on deployed code
Python applications should run as a dedicated user with the minimum necessary permissions. No root. No write access to the application directory. Use systemd’s ProtectSystem=strict and ReadWritePaths directives on Linux to enforce this at the OS level rather than hoping the application respects it.
6. Keep your Python version current
Python 3.11 reaches end-of-life in October 2027. Python 3.9 and 3.10 are already out of security support. Every month you run an EOL Python version, you accumulate unpatched vulnerabilities that will never be fixed in that release. Upgrade to 3.12 or 3.13 and automate the upgrade cycle so you are not scrambling when the next EOL deadline arrives.
7. Audit your code for path traversal and injection
Static analysis tools like Bandit catch common patterns, but the real threats are in custom code. Any function that constructs a file path from user input is a potential traversal vulnerability. Any function that passes user input to subprocess.run(shell=True) is a potential command injection. Review every instance manually — automated tools flag patterns but cannot understand intent.
Discussion
Leave a comment
No comments yet
Be the first to start the conversation.