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.

Supply chain layersDjango and PyPIpinned, auditedFrontend packagesnpm lock, auditBase imagerebuilt regularlyCI actionspinned by SHAEvery layer needs pinning, scanning, and an update routine, not just requirements.txt.
  1. Run a supported Django release (ideally the current LTS or latest) and subscribe to security announcements.
  2. Pin every dependency, including transitive ones, in a lock file with hashes; install with hash checking.
  3. Scan dependencies in CI (pip-audit, npm audit) and fail on known vulnerabilities above an agreed severity.
  4. Automate update pull requests (Dependabot or Renovate) and merge security updates quickly.
  5. Use private indexes safely: --index-url with a proxy, not --extra-index-url for internal names.
  6. Rebuild container images regularly on updated base images and scan them.
  7. 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.txt from pip-tools, uv.lock, or poetry.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.
the same install, every timedjango>=5.0✕different versions per build✕sub-dependencies float freely✕a hijacked release installs silentlyLock file with hashes✓every package pinned exactly✓hashes verified at install✓updates arrive as reviewed diffspip-tools, uv, and Poetry all produce hashed lock filesdjango-deployment.com
pip install --require-hashes refuses anything that does not match.

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=dev or 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.
advisory to productionAdvisory publishedCVE, GHSAScanner flags itpip-audit in CIUpdate PR openedDependabot, RenovateTests pass, mergelock file diffDeployedhours, not weeksAutomation shortens every step except the reviewdjango-deployment.com
Small, frequent updates make the security update just another routine merge.

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-url is 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-slim or 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.
every layer brings its own CVEsBase OS packagesrebuild often, slim imagesPython runtimepatch releasesPython dependenciesthe lock fileYour Django codereviews and testsAn unchanged Dockerfile still needs rebuilding to pick up OS fixesdjango-deployment.com
Scan the built image, not only requirements.txt, to see OS package advisories.

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

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.

Learning path · step 14 of 15

Hardening a Django deployment

Keep going