Django Dependency Security Checklist
Secure the software supply chain of a Django app: supported Django versions, pinned and hashed dependencies, vulnerability scanning, private index safety, container image updates, CI action pinning, and SBOMs.
Problem statement
A typical Django project runs far more third-party code than its own: Django, its dependencies, dozens of packages from PyPI, a base container image full of system libraries, npm packages for the frontend, and CI actions that run with access to secrets. Vulnerabilities and malicious packages in any of them become your vulnerabilities.
This checklist covers the controls that matter most for small and medium Django teams: staying on supported versions, reproducible and verified installs, automated vulnerability detection, safe package sources, and hygiene for containers and CI. Each item is concrete enough to verify.
- Run a supported Django release (ideally the current LTS or latest) and subscribe to security announcements.
- Pin every dependency, including transitive ones, in a lock file with hashes; install with hash checking.
- Scan dependencies in CI (
pip-audit,npm audit) and fail on known vulnerabilities above an agreed severity. - Automate update pull requests (Dependabot or Renovate) and merge security updates quickly.
- Use private indexes safely:
--index-urlwith a proxy, not--extra-index-urlfor internal names. - Rebuild container images regularly on updated base images and scan them.
- Pin CI actions to commit SHAs and give CI jobs minimal permissions.
Checklist
1. Django and Python versions
- The Django version receives security fixes (check Django's supported versions table); LTS releases get fixes for about three years.
- Python is a version still receiving security fixes.
- Someone receives Django security announcements (the django-announce mailing list or the Django blog's security category) and there is an agreed time to apply them (for example within a week, or days for critical issues).
- Deprecation warnings are reviewed in CI so upgrades are not blocked by years of accumulated changes.
2. Pinning and reproducibility
- A lock file pins every package and version:
requirements.txtfrom pip-tools,uv.lock, orpoetry.lock. - Hashes are recorded and installs verify them (
pip install --require-hashes, or tools that verify lock file hashes by default). - Production installs only runtime dependencies; development tools are in a separate group or file.
- The lock file is regenerated only through the tool, never edited by hand.
3. Vulnerability scanning
pip install pip-audit
pip-audit -r requirements.txt
pip-audit(or an equivalent) runs in CI on every pull request and on a schedule against the main branch, because new advisories appear for unchanged code.- A policy states which severities fail the build and how exceptions are recorded (with an expiry date).
- Frontend dependencies are scanned too (
npm audit --omit=devor equivalent). - Results are reviewed by a person, not just silenced.
4. Automated updates
- Dependabot or Renovate opens pull requests for outdated and vulnerable packages.
- Updates are grouped sensibly (for example weekly minor updates in one pull request, security updates immediately).
- The test suite is good enough that a green build means an update is probably safe; risky packages (Django itself, database drivers) get a staging deploy before production.
5. Package sources
- Internal packages come from a private index configured as the only index (
--index-url), which proxies PyPI, or with explicit source configuration in your tool. --extra-index-urlis not used for internal package names: pip may install a same-named public package with a higher version (dependency confusion).- New dependencies are reviewed before adding: maintenance activity, popularity, and the exact package name (typosquatting).
- Unused dependencies are removed; every package is attack surface.
6. Container images
- Base images are official, minimal (
python:3.x-slimor similar), and pinned to a tag (or digest) that you update deliberately. - Images are rebuilt regularly (for example weekly) even without code changes, so system library fixes are picked up.
- An image scanner (Trivy, Grype, or your registry's scanning) runs in CI and on stored images.
- Images run as a non-root user and contain no build tools or secrets.
7. CI and build pipeline
- Third-party GitHub Actions (or equivalents) are pinned to full commit SHAs, not moving tags, and updated through Dependabot.
- Workflow permissions are minimal (
permissions:set per job; read-only by default). - Secrets are only available to jobs that need them, and never to workflows running untrusted pull request code.
- Build artifacts (images) are produced by CI, not developers' laptops.
8. Inventory
- An SBOM (software bill of materials) can be generated for each release, for example with CycloneDX tools for Python and your image scanner for containers, so you can answer "are we affected?" quickly when a new vulnerability is announced.
- Licences of dependencies are reviewed for compatibility with your use.
Explanation
Supply chain security rests on three ideas. Know exactly what you run (lock files, hashes, SBOMs). Find out quickly when something you run is vulnerable (scanning on a schedule, security announcements). Be able to change it safely and fast (automated update pull requests, a test suite, and a routine release process). Package sources and CI permissions close the two paths attackers use to insert code directly: tricking the installer and compromising the pipeline.
Edge cases / notes
Vendored or forked packages
Forked packages do not receive upstream security fixes automatically. Track the upstream project and merge fixes, or plan to return to the upstream package.
False positives and unfixable advisories
Some advisories do not affect your usage, or have no fix yet. Record an exception with a reason and an expiry date instead of disabling the scanner.
When to automate this
Almost everything here is automatable: scheduled scanning, update pull requests, image rebuilds, SHA-pinning updates, and SBOM generation per release. The human part is reviewing and merging promptly, which is easier when updates arrive small and often.
Next steps
- Set up update automation with Automate Django Dependency and Security Updates.
- Pin and verify installs with Pin Django Dependencies with pip-tools and Hashes.
- Plan major version moves with the Django Version Upgrade Checklist for Production.
- Keep the operating system patched with Enable Automatic Security Updates on a Django Server.
FAQ
How do I check my Django project for vulnerable dependencies?
Run pip-audit -r requirements.txt (or against your environment) in CI and on a schedule, and scan frontend and container dependencies with their own tools.
Is pinning dependencies enough for security?
No. Pinning makes installs reproducible. You also need scanning to detect vulnerabilities and an update process to apply fixes.
What is dependency confusion?
Installing a public package with the same name as an internal one, because the installer searched public and private indexes. Use a single private index that proxies PyPI.
Should GitHub Actions be pinned to commit SHAs?
Yes, for third-party actions. Tags can be moved; a SHA refers to exact code. Let an update bot keep the SHAs current.