Why Bandit for Python Security?
Bandit is an open-source SAST tool developed by the OpenStack Security Project that specializes in analyzing Python code for common security issues. It's particularly valuable because:
- Python-specific analysis - Understands Python idioms and common patterns
- Plugin-based architecture - Extensible with custom checks
- CI/CD friendly - Designed for automation
- Zero cost - Completely free and open-source
Common Vulnerabilities Bandit Detects
- SQL injection vulnerabilities
- Shell injection risks
- Hardcoded passwords and secrets
- Use of insecure modules
- Input validation issues
- Information disclosure risks
Hands-On: Implementing Bandit
Installation
# Install using pip pip install bandit # Or install from source git clone https://github.com/PyCQA/bandit.git cd bandit pip install .
Basic Usage
# Scan a single file bandit my_script.py # Scan an entire directory bandit -r my_project/ # Generate HTML report bandit -r my_project/ -f html -o report.html # Scan with specific security level bandit -r my_project/ -l high
Sample Vulnerable Python Code
# vulnerable_app.py import os import pickle import subprocess import sqlite3 def process_user_input(): # Vulnerability: Code injection user_input = input("Enter command: ") os.system(user_input) # B602: subprocess_popen_with_shell_equals_true def database_operations(): # Vulnerability: SQL injection username = input("Enter username: ") conn = sqlite3.connect('users.db') cursor = conn.cursor() cursor.execute(f"SELECT * FROM users WHERE username = '{username}'") # B608: hardcoded_sql_expressions def data_deserialization(): # Vulnerability: Insecure deserialization data = input("Enter serialized data: ") obj = pickle.loads(data.encode()) # B301: blacklist
Running Bandit Scan
bandit -r vulnerable_app.py -f txt
Sample Bandit Output
Results generated: >> Issue: [B602:subprocess_popen_with_shell_equals_true] Using subprocess with shell=True Severity: High Confidence: High Location: vulnerable_app.py:8 More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b602-subprocess-popen-with-shell-equals-true 7 user_input = input("Enter command: ") 8 os.system(user_input) >> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction. Severity: Medium Confidence: Medium Location: vulnerable_app.py:15 15 cursor.execute(f"SELECT * FROM users WHERE username = '{username}'") >> Issue: [B301:blacklist] Use of unsafe deserialization function. Severity: High Confidence: High Location: vulnerable_app.py:20 20 obj = pickle.loads(data.encode())
Advanced Bandit Configuration
Custom Configuration File
# bandit.yml exclude_dirs: ['tests', 'venv', 'migrations'] skips: ['B101', 'B102'] tests: ['B301', 'B302', 'B601', 'B602'] targets: - src/ - app/ output_format: json verbose: true
Using Configuration File
bandit -c bandit.yml -r my_project/
CI/CD Integration (GitHub Actions)
name: Security Scan with Bandit on: [push, pull_request] jobs: security-scan: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.9' - name: Install Bandit run: pip install bandit - name: Run Bandit Security Scan run: bandit -r . -f json -o bandit-report.json - name: Upload Bandit Report uses: actions/upload-artifact@v3 with: name: bandit-report path: bandit-report.json
Bandit Test Types and Severity Levels
Severity Levels
- Low: Code quality issues, minor security concerns
- Medium: Potential security vulnerabilities
- High: Critical security vulnerabilities
Common Test IDs
- B1xx: Various general tests
- B2xx: Application/framework-specific issues
- B3xx: Blacklisted imports and functions
- B4xx: Using insecure random generators
- B5xx: SSL/TLS issues
- B6xx: Shell injection vulnerabilities
- B7xx: Pickle and YAML deserialization
Custom Bandit Plugins
Creating Custom Tests
# custom_checks.py import bandit from bandit.core import test_properties as test @test.checks('Call') @test.test_id('B901') def hardcoded_api_key(context): """Check for hardcoded API keys""" suspicious_strings = ['api_key', 'secret_key', 'password'] if context.call_function_name_qual in suspicious_strings: return bandit.Issue( severity=bandit.HIGH, confidence=bandit.MEDIUM, text="Potential hardcoded API key detected" )
Using Custom Plugins
bandit -r my_project/ -p custom_checks.py
Best Practices for Bandit Implementation
1. Integrate Early in Development
# Pre-commit hook example # .git/hooks/pre-commit #!/bin/bash bandit -r . -l high -i
2. Regular Scheduled Scans
# GitHub Actions scheduled scan on: schedule: - cron: '0 2 * * 1' # Weekly scan
3. Baseline Establishment
# Establish baseline ignoring existing issues bandit -r . --baseline baseline.json
4. Quality Gates
# Fail build on high severity issues bandit -r . -l high --exit-zero
Limitations and Considerations
While Bandit is powerful, it's important to understand its limitations:
- Static analysis only - Cannot detect runtime issues
- Python-specific - Only works with Python code
- Pattern-based - May produce false positives/negatives
- No data flow analysis - Limited context awareness
Conclusion
Bandit provides an excellent open-source SAST solution for Python applications. Its ease of use, comprehensive vulnerability detection, and seamless CI/CD integration make it an essential tool for any Python developer concerned with security.
By implementing Bandit in your development workflow, you can catch common security issues early, reduce remediation costs, and build more secure Python applications.
Top comments (0)