Tools: Latest: 5 Python Security Tools Every Developer Should Know

Tools: Latest: 5 Python Security Tools Every Developer Should Know

1. Bandit — Python Code Security Linter

Installation and Basic Usage

What Bandit Catches

Configuration

Integration with CI/CD

2. Safety — Dependency Vulnerability Scanner

Key Features

Automated Scanning

3. Cryptography — Secure Cryptographic Primitives

Best Practices

4. Semgrep — Pattern-Based Security Analysis

Why Semgrep Over Traditional Linters

5. Trivy — Container and Filesystem Scanner

Docker Integration Security isn't something you bolt on after the fact. It's a mindset, a practice, and increasingly, a set of automated tools that catch vulnerabilities before they reach production. Python's ecosystem has matured significantly in recent years, and the security tooling available today is both powerful and accessible. Here are five essential Python security tools that every developer should have in their toolkit. Bandit is a security linter specifically designed for Python code. It scans your codebase for common security issues like hardcoded passwords, SQL injection vulnerabilities, and insecure function usage. Bandit checks for over 200 potential security issues, including: Create a bandit.yml configuration file for project-specific rules: Safety checks your project's dependencies against a database of known vulnerabilities. It's essential for catching supply chain attacks and outdated packages with known CVEs. The cryptography library is the standard for implementing secure encryption, hashing, and signing in Python. It provides a high-level interface while being backed by OpenSSL. Semgrep goes beyond simple linting. It uses a powerful pattern language to detect complex security vulnerabilities that require understanding code semantics. If you're deploying Python applications in containers (and you probably should be), Trivy is an essential tool for scanning container images and filesystems for vulnerabilities. For the complete guide with all code examples and advanced patterns, read the full article on our blog. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 500;">pip -weight: 500;">install bandit bandit -r my_project/ -weight: 500;">pip -weight: 500;">install bandit bandit -r my_project/ -weight: 500;">pip -weight: 500;">install bandit bandit -r my_project/ skips: ['B101', 'B601'] exclude_dirs: - tests/ - migrations/ skips: ['B101', 'B601'] exclude_dirs: - tests/ - migrations/ skips: ['B101', 'B601'] exclude_dirs: - tests/ - migrations/ # GitHub Actions example - name: Security Check run: | -weight: 500;">pip -weight: 500;">install bandit bandit -r src/ -f json -o bandit-report.json if [ $? -ne 0 ]; then echo "::warning::Security issues found. Check bandit-report.json" fi # GitHub Actions example - name: Security Check run: | -weight: 500;">pip -weight: 500;">install bandit bandit -r src/ -f json -o bandit-report.json if [ $? -ne 0 ]; then echo "::warning::Security issues found. Check bandit-report.json" fi # GitHub Actions example - name: Security Check run: | -weight: 500;">pip -weight: 500;">install bandit bandit -r src/ -f json -o bandit-report.json if [ $? -ne 0 ]; then echo "::warning::Security issues found. Check bandit-report.json" fi -weight: 500;">pip -weight: 500;">install safety safety check --full-report -weight: 500;">pip -weight: 500;">install safety safety check --full-report -weight: 500;">pip -weight: 500;">install safety safety check --full-report # safety_check.py import subprocess import json def check_dependencies(): result = subprocess.run( ["safety", "check", "--json"], capture_output=True, text=True ) if result.returncode != 0: vulnerabilities = json.loads(result.stdout) for vuln in vulnerabilities: print(f"[CRITICAL] {vuln['package']} {vuln['version']}") print(f" Vulnerability: {vuln['advisory']}") print(f" Fix: Upgrade to {vuln['fixed_version']}") return False return True # safety_check.py import subprocess import json def check_dependencies(): result = subprocess.run( ["safety", "check", "--json"], capture_output=True, text=True ) if result.returncode != 0: vulnerabilities = json.loads(result.stdout) for vuln in vulnerabilities: print(f"[CRITICAL] {vuln['package']} {vuln['version']}") print(f" Vulnerability: {vuln['advisory']}") print(f" Fix: Upgrade to {vuln['fixed_version']}") return False return True # safety_check.py import subprocess import json def check_dependencies(): result = subprocess.run( ["safety", "check", "--json"], capture_output=True, text=True ) if result.returncode != 0: vulnerabilities = json.loads(result.stdout) for vuln in vulnerabilities: print(f"[CRITICAL] {vuln['package']} {vuln['version']}") print(f" Vulnerability: {vuln['advisory']}") print(f" Fix: Upgrade to {vuln['fixed_version']}") return False return True from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import base64 # Symmetric encryption key = Fernet.generate_key() cipher = Fernet(key) encrypted = cipher.encrypt(b"sensitive data") decrypted = cipher.decrypt(encrypted) # Secure password hashing from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes import os salt = os.urandom(16) kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=480000, ) key = kdf.derive(b"password") from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import base64 # Symmetric encryption key = Fernet.generate_key() cipher = Fernet(key) encrypted = cipher.encrypt(b"sensitive data") decrypted = cipher.decrypt(encrypted) # Secure password hashing from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes import os salt = os.urandom(16) kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=480000, ) key = kdf.derive(b"password") from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import base64 # Symmetric encryption key = Fernet.generate_key() cipher = Fernet(key) encrypted = cipher.encrypt(b"sensitive data") decrypted = cipher.decrypt(encrypted) # Secure password hashing from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes import os salt = os.urandom(16) kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=480000, ) key = kdf.derive(b"password") -weight: 500;">pip -weight: 500;">install semgrep semgrep --config=auto --security-severity=WARNING . -weight: 500;">pip -weight: 500;">install semgrep semgrep --config=auto --security-severity=WARNING . -weight: 500;">pip -weight: 500;">install semgrep semgrep --config=auto --security-severity=WARNING . # Semgrep rule example # rules/no-hardcoded-secrets.yml rules: - id: detect-hardcoded-api-key patterns: - pattern-either: - pattern: $X = "$API_KEY" - pattern: $X = "$SECRET" - pattern: $X = "$TOKEN" message: "Possible hardcoded API key detected" severity: ERROR languages: [python] # Semgrep rule example # rules/no-hardcoded-secrets.yml rules: - id: detect-hardcoded-api-key patterns: - pattern-either: - pattern: $X = "$API_KEY" - pattern: $X = "$SECRET" - pattern: $X = "$TOKEN" message: "Possible hardcoded API key detected" severity: ERROR languages: [python] # Semgrep rule example # rules/no-hardcoded-secrets.yml rules: - id: detect-hardcoded-api-key patterns: - pattern-either: - pattern: $X = "$API_KEY" - pattern: $X = "$SECRET" - pattern: $X = "$TOKEN" message: "Possible hardcoded API key detected" severity: ERROR languages: [python] # Install Trivy -weight: 500;">curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/-weight: 500;">install.sh | sh -s -- -b /usr/local/bin # Scan a container image trivy image my-python-app:latest # Scan a filesystem trivy fs /path/to/project # Install Trivy -weight: 500;">curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/-weight: 500;">install.sh | sh -s -- -b /usr/local/bin # Scan a container image trivy image my-python-app:latest # Scan a filesystem trivy fs /path/to/project # Install Trivy -weight: 500;">curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/-weight: 500;">install.sh | sh -s -- -b /usr/local/bin # Scan a container image trivy image my-python-app:latest # Scan a filesystem trivy fs /path/to/project # Multi-stage build with security scanning FROM python:3.12-slim as builder COPY requirements.txt . RUN -weight: 500;">pip -weight: 500;">install --user -r requirements.txt FROM python:3.12-slim COPY --from=builder /root/.local /root/.local COPY . . # Multi-stage build with security scanning FROM python:3.12-slim as builder COPY requirements.txt . RUN -weight: 500;">pip -weight: 500;">install --user -r requirements.txt FROM python:3.12-slim COPY --from=builder /root/.local /root/.local COPY . . # Multi-stage build with security scanning FROM python:3.12-slim as builder COPY requirements.txt . RUN -weight: 500;">pip -weight: 500;">install --user -r requirements.txt FROM python:3.12-slim COPY --from=builder /root/.local /root/.local COPY . . # Scan during build trivy image --exit-code 1 --severity CRITICAL,HIGH my-python-app:latest # Scan during build trivy image --exit-code 1 --severity CRITICAL,HIGH my-python-app:latest # Scan during build trivy image --exit-code 1 --severity CRITICAL,HIGH my-python-app:latest - Hardcoded passwords and secrets: B105 and B106 rules catch hardcoded passwords and passwords in function defaults - SQL injection: Detects string formatting in SQL queries - Insecure YAML loading: Flags yaml.load() without a safe loader - Shell injection: Catches unsafe use of subprocess and os.system - Insecure temp files: Detects use of tempfile.mktemp instead of mkstemp - Scans against PyUp's vulnerability database with over 300,000 entries - Supports requirements.txt, Pipfile, Pipfile.lock, and setup.py - Can be integrated into CI/CD pipelines - Provides detailed CVE information and remediation advice - Always use Fernet for symmetric encryption (it handles IVs and authentication) - Use PBKDF2 with at least 480,000 iterations for password hashing - Never roll your own crypto — use the library's high-level APIs - Store salts alongside hashes but never reuse them - Tainted data flow: Track user input from HTTP endpoints to database queries - Authentication bypass: Detect missing auth checks on protected endpoints - Insecure deserialization: Flag pickle.loads() on untrusted data - Path traversal: Catch unsanitized file path operations