Skip to main content

Common Misconfigurations

  1. Using outdated packages with known CVEs
  2. Not scanning dependencies for vulnerabilities
  3. Ignoring security advisories
  4. Missing dependency pinning
  5. Not checking package integrity

Vulnerable Example

# requirements.txt with vulnerable packages
# Vulnerable Django version (CVE-2023-23969)
Django==3.2.0

# Old Pillow with security issues
Pillow==8.1.0

# Vulnerable PyYAML (CVE-2020-1747)
PyYAML==5.3

# Insecure requests version
requests==2.20.0

# Vulnerable Jinja2
Jinja2==2.11.2

# Old cryptography package
cryptography==3.2

# No hash verification
# No source verification
# setup.py with loose version constraints
from setuptools import setup

setup(
    name="vulnerable-app",
    install_requires=[
        "Django>=3.0"# Too permissive
        "Pillow",       # No version constraint
        "PyYAML<6",     # Allows vulnerable versions
        "requests"      # Unpinned
    ]
)

Secure Solution

# This file is autogenerated by pip-compile with hashes
# To update, run:

# pip-compile --generate-hashes requirements.in -o requirements.txt

# requirements.txt with secure, pinned versions
# Secure versions with hash verification
Django==4.2.9 \
    --hash=sha256:abcdef1234567890abcdef1234567890

Pillow==10.2.0 \
    --hash=sha256:1234567890abcdef1234567890abcdef

PyYAML==6.0.1 \
    --hash=sha256:bfdf1234567890abcdef1234567890abcd

requests==2.31.0 \
    --hash=sha256:58cd1234567890abcdef1234567890abcd

Jinja2==3.1.3 \
    --hash=sha256:ac8bd1234567890abcdef1234567890abcd

cryptography==41.0.7 \
    --hash=sha256:13abd1234567890abcdef1234567890abcd
# requirements-dev.txt for development dependencies
# Security scanning tools
safety==3.0.1
pip-audit==2.6.1
bandit==1.7.5

# For generating requirements
pip-tools==7.4.1
# .pip.conf for security settings
[global]
require-hashes = true
no-cache-dir = false
disable-pip-version-check = false

[install]
require-virtualenv = true
# .github/workflows/security.yml
name: Python Security Check
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
      
      - name: Run safety check
        run: safety check --json
      
      - name: Run pip-audit
        run: pip-audit
      
      - name: Run Bandit
        run: bandit -r src/

Key Commands for Updating

Here are the essential commands for securely managing your Python dependencies.

1. Audit for Vulnerabilities

Use these tools (listed in requirements-dev.txt) to scan your project.
# Using pip-audit (official Python tool)
pip-audit

# Using safety
safety check

2. Generate Pinned Requirements (The Secure Way)

This is the most secure method. It uses pip-tools to generate a locked requirements.txt from a simple requirements.in file. Step 1: Create a requirements.in file with your top-level dependencies:
# requirements.in
Django
Pillow
PyYAML
requests
Jinja2
cryptography
Step 2: Install pip-tools (usually from requirements-dev.txt):
pip install pip-tools
Step 3: Compile the file, generating hashes. This creates the secure requirements.txt file.
pip-compile --generate-hashes requirements.in -o requirements.txt
To update all packages later, just re-run this command.

3. Install from Secure Requirements

This command installs the exact versions from your generated file. If your .pip.conf is set up, it will fail if any hashes don’t match.
pip install -r requirements.txt

4. Sync Your Environment

A better way to install is using pip-sync, which comes with pip-tools. It installs only what’s in requirements.txt and removes anything else, perfectly syncing your environment.
pip-sync

5. Check for Outdated Packages

This command lists any packages that have newer versions available.
pip list --outdated

Best Practices

  • Use tools like Safety, pip-audit, or Snyk.
  • Pin exact versions in production.
  • Use pip-tools to generate pinned requirements.txt files with hashes.
  • Regularly update dependencies by re-compiling your requirements.txt.
  • Use virtual environments.