Skip to main content

Common Misconfigurations

  1. Using version ranges instead of exact versions
  2. Not using requirements.txt in production
  3. Missing version constraints in setup.py
  4. Not locking transitive dependencies
  5. Mixing pip and conda without constraints

Vulnerable Example

# requirements.txt with unpinned versions
Django>=3.0  # Allows any version above 3.0
requests     # No version specified
numpy>1.19   # Open-ended range
pandas~=1.3  # Compatible release clause
scipy        # Latest version always
Pillow<9     # Only upper bound
# setup.py with loose constraints
from setuptools import setup

setup(
    name="unpinned-app",
    install_requires=[
        "flask",           # No version
        "sqlalchemy>=1.0", # Open range
        "celery>4,<6",     # Wide range
        "redis",           # Unpinned
    ],
    extras_require={
        "dev": [
            "pytest",      # No version
            "black",       # No version
        ]
    }
)
# Direct pip install without constraints
pip install django requests numpy
# No lock file generation

Secure Solution

# requirements.txt with pinned versions
# Generated with: pip freeze > requirements.txt
Django==4.2.9
requests==2.31.0
numpy==1.26.3
pandas==2.1.4
scipy==1.11.4
Pillow==10.2.0

# Include all transitive dependencies
certifi==2023.11.17
charset-normalizer==3.3.2
idna==3.6
urllib3==2.1.0
# requirements.in for pip-tools
# Source file for pip-compile
Django==4.2.9
requests==2.31.0
numpy==1.26.3
pandas==2.1.4
# Generate locked requirements
pip-compile --generate-hashes requirements.in -o requirements.txt
# setup.py with proper constraints
from setuptools import setup

# Read requirements from file
with open('requirements.txt') as f:
    requirements = f.read().splitlines()

setup(
    name="secure-app",
    version="1.0.0",
    install_requires=requirements,
    python_requires=">=3.9,<3.12"# Specify Python version
)
# pyproject.toml for modern Python projects
[project]
name = "secure-app"
version = "1.0.0"
requires-python = ">=3.9,<3.12"
dependencies = [
    "Django==4.2.9",
    "requests==2.31.0",
    "numpy==1.26.3",
    "pandas==2.1.4",
]

[tool.pip-tools]
generate-hashes = true
allow-unsafe = false

[tool.poetry.dependencies]
python = "^3.9"
Django = "4.2.9"
requests = "2.31.0"
# Dependabot configuration
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
    reviewers:
      - "security-team"
    labels:
      - "dependencies"
      - "security"

Key Commands for Pinning

Here are the commands that power the secure workflows described above.

1. Simple Pinning (pip freeze)

This command “freezes” the current virtual environment, capturing the exact versions of everything installed and saving it to a file.
pip freeze > requirements.txt
  • Pro: Very simple.
  • Con: Not reproducible. It captures all dependencies in your environment, not just the ones your project needs. It’s hard to update.

2. Modern Pinning (pip-tools)

This is the recommended approach. It uses pip-compile to generate a locked requirements.txt from a simple requirements.in file. Step 1: Install pip-tools
pip install pip-tools
Step 2: Create requirements.in Create a file listing only your direct dependencies (e.g., django, requests). Step 3: Compile the lock file This command resolves all dependencies (direct and transitive) and pins them with hashes.
pip-compile requirements.in -o requirements.txt --generate-hashes

3. Installing Pinned Dependencies

This command installs only the exact versions specified in your lock file. It is the standard command for production and CI/CD.
pip install -r requirements.txt

4. Syncing Your Environment

This command (from pip-tools) is even better. It makes your virtual environment exactly match the requirements.txt file, installing missing packages and uninstalling any that don’t belong.
pip-sync

Best Practices

  • Use pip-tools or Poetry for dependency management.
  • Generate requirements.txt with exact versions.
  • Include hash verification.
  • Use separate files for dev and prod dependencies.
  • Implement automated dependency updates with review.