Bandit
相关工具推荐
| 工具 | 说明 |
|---|---|
| Ruff | 内置 flake8-bandit 兼容规则(S 系列),可替代 Bandit 的部分检查 |
| Pylint | 包含部分安全相关检查,可与 Bandit 互补 |
| Mypy | 静态类型安全,与 Bandit 的运行时安全检查互补 |
| Gitleaks | 检测 Git 仓库中的密钥泄露,与 Bandit 的代码安全检查互补 |
| Detect-Secrets | Yelp 出品的密钥泄露基线扫描工具 |
| CodeQL | GitHub 官方代码分析引擎,支持 Python 安全分析 |
| Gosec | Go 生态的安全扫描工具,定位类似 |
1. 简介
Bandit 是由 PyCQA 维护的 Python 代码安全扫描工具,专门用于发现 Python 代码中的常见安全问题。Bandit 通过解析每个源文件构建 AST(抽象语法树),然后对 AST 节点运行插件化检测规则,最终生成安全报告。
- 主要检查语言:Python
- 主要检查能力:检测 Python 代码中的安全隐患(硬编码密码、注入风险、弱加密、子进程调用等);涵盖安全类检查
- 核心检查原理:基于 Python AST 遍历,插件化检测规则架构,支持自定义扩展
- 检查规则/选项:34 个 B 规则(B1xx 杂项、B2xx 框架配置、B5xx 密码学、B6xx 注入、B7xx XSS),全量规则列表
- GitHub 仓库:https://github.com/PyCQA/bandit(8,084 stars)
- 开源协议:Apache-2.0
- 最新稳定版本:v1.8.3
- 运行环境要求:Python 3.9+
- 误报率:低
2. 官方文档
3. 社区优秀实践
3.1 OpenStack Keystone
OpenStack Keystone 是 OpenStack 的身份认证服务,是 Bandit 的原始诞生项目之一。Keystone 在 pre-commit 中集成了 Bandit,并在 pyproject.toml 中配置了 Bandit 规则。
- pre-commit 配置:在
.pre-commit-config.yaml中使用https://github.com/PyCQA/bandit仓库,指定rev: '1.8.3',通过args: ['-c', 'pyproject.toml']引用配置文件 - Bandit 配置:在
pyproject.toml的[tool.bandit]节中配置exclude_dirs,排除测试和开发目录 - 生态参考项目:openstack/keystone
3.2 PyCQA Bandit 官方仓库
Bandit 自身仓库同时维护了 .pre-commit-hooks.yaml,使其他项目可以直接通过 pre-commit 引用 Bandit 作为 hook。这是 Bandit 官方推荐的 pre-commit 集成方式。
- pre-commit hooks 定义:
.pre-commit-hooks.yaml定义了bandithook,使用language: python、language_version: python3,限制types: [python],设置require_serial: true避免并行冲突 - 生态参考项目:PyCQA/bandit
3.3 PyCQA bandit-action(GitHub Actions 官方 Action)
PyCQA 官方提供了 bandit-action GitHub Action,支持 Code Scanning 结果上传,被超过 270 个仓库使用。
- 功能:自动运行 Bandit 扫描,支持 SARIF 格式输出并上传至 GitHub Code Scanning
- 配置项:支持
configfile、profile、tests、skips、severity、confidence、exclude、baseline、targets等输入参数 - 生态参考项目:PyCQA/bandit-action
4. 工具配置说明
4.1 配置文件说明
Bandit 支持多种配置文件格式:
| 配置文件 | 格式 | 用途 | 使用场景 |
|---|---|---|---|
.bandit |
INI | 项目级默认配置 | 放在项目根目录,使用 -r 时自动加载 |
bandit.yaml |
YAML | 显式指定的配置文件 | 通过 -c bandit.yaml 指定 |
pyproject.toml |
TOML | 与项目其他 Python 工具配置统一管理 | 通过 -c pyproject.toml 指定,需安装 bandit[toml] |
注意:
.banditINI 文件仅在 Bandit 使用-r选项时自动查找。如果使用其他文件名或格式,必须通过-c或--ini显式指定。
4.2 bandit.yaml 配置详解
Bandit 的配置项在不同格式下语义一致,仅语法不同。以下配置项适用于 .bandit(INI)、bandit.yaml(YAML)和 pyproject.toml(TOML [tool.bandit] 段)三种格式。
配置项说明:
| 配置项 | 类型 | 说明 |
|---|---|---|
exclude_dirs |
string[] | 排除非业务代码的目录列表(INI 格式中对应 exclude,值为逗号分隔字符串) |
tests |
string[] | 仅运行的检测规则 ID 列表 |
skips |
string[] | 跳过的检测规则 ID 列表 |
最小配置示例(三种格式对照):
INI 格式(.bandit):
# .bandit
[bandit]
exclude = tests,path/to/file
tests = B201,B301
skips = B101,B601
YAML 格式(bandit.yaml):
# bandit.yaml
exclude_dirs: ["tests", "path/to/file"]
tests: ["B201", "B301"]
skips: ["B101", "B601"]
TOML 格式(pyproject.toml):
# pyproject.toml
[tool.bandit]
exclude_dirs = ["tests", "path/to/file"]
tests = ["B201", "B301"]
skips = ["B101", "B601"]
默认全量扫描:
对于大多数项目,建议先使用 Bandit 的默认全量扫描评估结果,再根据实际情况调整配置:
bandit -r . -f json -o bandit-report.json
推荐配置示例:
以下配置跳过常见的误报规则,同时保留所有高价值的安全检查(以 pyproject.toml 为例):
[tool.bandit]
# 排除非业务代码目录
exclude_dirs = [
"tests",
"test_*",
"migrations",
"venv",
".venv",
"__pycache__",
".tox",
"build",
"dist",
"*.egg-info",
]
# 跳过常见误报规则:
# B101: assert_used -- assert 在测试中广泛使用,生产代码中也有合理用途
# B603: subprocess_without_shell_equals_true -- 不带 shell=True 的 subprocess 本身较安全
# B606: start_process_with_no_shell -- 同上
skips = ["B101", "B603", "B606"]
4.3 规则 ID 分组说明
Bandit 的检测规则按 ID 范围分组:
| ID 范围 | 分类 | 说明 |
|---|---|---|
| B1xx | 杂项检查 | assert 使用、exec 使用、硬编码密码/路径/端口、try-except-pass 等 |
| B2xx | 应用/框架配置 | Flask debug 模式等 |
| B3xx | 黑名单(函数调用) | 不安全的函数调用黑名单 |
| B4xx | 黑名单(模块导入) | 不安全的模块导入黑名单 |
| B5xx | 加密相关 | SSL/TLS 配置、弱密钥、不安全的 YAML 加载、SSH 配置等 |
| B6xx | 注入攻击 | shell 注入、SQL 注入、Paramiko 调用等 |
| B7xx | XSS | Jinja2 自动转义、Mako 模板、Django mark_safe 等 |
4.4 高优先级规则说明
以下规则建议在所有项目中启用,覆盖了最常见的 Python 安全漏洞:
| 规则 ID | 名称 | 严重级别 | 说明 |
|---|---|---|---|
| B105 | hardcoded_password_string | High | 检测硬编码的密码字符串 |
| B106 | hardcoded_password_funcarg | High | 检测函数参数中的硬编码密码 |
| B107 | hardcoded_password_default | High | 检测函数默认参数中的硬编码密码 |
| B108 | hardcoded_tmp_directory | Low | 检测硬编码的临时目录路径 |
| B110 | try_except_pass | Low | 检测空 except 块(可能吞没安全异常) |
| B112 | try_except_continue | Low | 检测 except 块中的 continue(可能跳过安全处理) |
| B201 | flask_debug_true | High | 检测 Flask debug 模式开启 |
| B501 | request_with_no_cert_validation | High | 检测未验证证书的 HTTP 请求 |
| B502 | ssl_with_bad_version | High | 检测不安全的 SSL/TLS 版本 |
| B503 | ssl_with_bad_defaults | Medium | 检测不安全的 SSL/TLS 默认配置 |
| B504 | ssl_with_no_version | High | 检测未指定版本的 SSL/TLS |
| B505 | weak_cryptographic_key | Medium | 检测弱加密密钥 |
| B506 | yaml_load | Medium | 检测不安全的 yaml.load 调用 |
| B507 | ssh_no_host_key_verification | High | 检测未验证 SSH 主机密钥 |
| B601 | paramiko_calls | Medium | 检测不安全的 Paramiko 调用 |
| B602 | subprocess_popen_with_shell_equals_true | High | 检测 shell=True 的 subprocess 调用 |
| B607 | start_process_with_partial_path | Medium | 检测使用部分路径启动进程 |
| B608 | hardcoded_sql_expressions | Medium | 检测硬编码的 SQL 表达式 |
| B609 | linux_commands_wildcard_injection | High | 检测 Linux 命令通配符注入 |
| B610 | django_extra_used | Medium | 检测 Django extra() 中的 SQL 注入风险 |
| B611 | django_rawsql_used | Medium | 检测 Django raw() 中的 SQL 注入风险 |
| B701 | jinja2_autoescape_false | High | 检测 Jinja2 自动转义关闭 |
| B702 | use_of_mako_templates | Medium | 检测 Mako 模板使用(默认不转义) |
| B703 | django_mark_safe | Medium | 检测 Django mark_safe 使用 |
完整规则列表请参考:Bandit 检测插件完整列表 和 黑名单插件
5. 主流集成方式
5.1 pip / uv 安装(生态主流)
Bandit 是 Python 工具,其生态主流安装方式为 pip/uv。
# 使用 pip 安装
pip install bandit
# 使用 uv 安装
uv pip install bandit
# 如需 TOML 配置文件支持(pyproject.toml)
pip install bandit[toml]
# 如需 SARIF 输出格式支持
pip install bandit[sarif]
# 如需 bandit-baseline CLI
pip install bandit[baseline]
pyproject.toml 中声明为开发依赖:
[project.optional-dependencies]
dev = ["bandit[toml]>=1.9.4"]
5.2 pre-commit 集成
Bandit 官方仓库提供了 .pre-commit-hooks.yaml,可直接引用。
适用前提: pre-commit 会自动创建隔离的 Python 虚拟环境并安装 Bandit,无需项目本地预先安装 Bandit。如需使用自定义配置文件(如 pyproject.toml),需通过 additional_dependencies 添加 bandit[toml]。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/PyCQA/bandit
rev: "1.9.4" # 建议锁定版本
hooks:
- id: bandit
# 如需使用 pyproject.toml 配置,取消下面注释
# args: ["-c", "pyproject.toml"]
# additional_dependencies: ["bandit[toml]"]
说明:pre-commit 默认仅将暂存区中变更的文件传给 hook(文件级增量),因此 Bandit 通过 pre-commit 运行时天然只检查变更文件。
安装并启用 hook:
pre-commit install
pre-commit run bandit --all-files # 手动全量运行
增量检查:文件级增量检查优先基于 pre-commit 框架实现。pre-commit 默认仅将暂存区中变更的文件传给 hook,Bandit 通过 pre-commit 运行时天然只检查变更的 Python 文件。
5.3 IDE 集成
VS Code:
安装扩展 Bandit by PyCQA,在 VS Code 中实时显示 Bandit 安全检查结果。
Vim/Neovim:
通过 ALE (Asynchronous Lint Engine) 集成 Bandit。ALE 支持 Bandit 作为 Python linter,在编辑时自动运行检查。
Sublime Text:
通过 SublimeLinter-bandit 插件集成。
Emacs:
通过 flycheck-pycheckers 集成。
5.4 命令行使用方式
# 递归扫描项目目录
bandit -r path/to/your/code
# 扫描指定文件
bandit examples/*.py
# 只报告高危问题
bandit -r . --severity-level=high
bandit -r . -lll
# 指定上下文行数
bandit examples/*.py -n 3
# 使用配置文件
bandit -c pyproject.toml -r .
# 使用 .bandit INI 文件(需配合 -r 使用)
bandit -r .
# 指定 INI 文件路径
bandit --ini tox.ini -r .
# 跳过指定测试规则
bandit -r . -s B101,B601
# 只运行指定测试规则
bandit -r . -t B201,B301
# 使用 profile
bandit examples/*.py -p ShellInjection
# 从标准输入读取
cat examples/imports.py | bandit -
# 输出为 JSON 格式(用于基线对比)
bandit -f json -o bandit-report.json -r .
# 使用基线对比
bandit -b baseline.json -r .
# 指定排除目录
bandit -r . --exclude "tests/,venv/"
# 查看帮助
bandit -h
检查模式 vs 修复模式: Bandit 是纯检查工具,不提供自动修复功能。发现安全问题后需手动修复代码,或通过 # nosec 注释抑制误报。
增量检查 -- Bandit 本身不提供专门的增量参数(如 --new-from-rev),但可以直接将文件列表传给 Bandit:
# 直接指定文件列表
bandit file1.py file2.py path/to/module.py
# 使用 glob 模式
bandit "src/**/*.py"
配合 Git 获取变更文件列表实现增量扫描:
# 只检查 Git 暂存区中变更的 Python 文件
git diff --name-only --diff-filter=ACMR -- '*.py' | xargs bandit
# 只检查相对于指定 commit 变更的 Python 文件
git diff --name-only --diff-filter=ACMR main -- '*.py' | xargs bandit
# 配合配置文件
git diff --name-only --diff-filter=ACMR -- '*.py' | xargs bandit -c pyproject.toml
CI 脚本调用:
GitHub Actions(手动安装方式)
# .github/workflows/bandit.yml
name: Bandit
on: [push, pull_request]
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install bandit[toml]
- run: bandit -c pyproject.toml -r . -f json -o bandit-report.json
- run: bandit -c pyproject.toml -r .
GitLab CI
# .gitlab-ci.yml
bandit:
image: python:3.12
before_script:
- pip install bandit[toml]
script:
- bandit -c pyproject.toml -r . -f json -o bandit-report.json
- bandit -c pyproject.toml -r .
artifacts:
reports:
codequality: bandit-report.json
when: always
增量检查 -- 按 PR/MR 变更文件触发增量扫描:
GitHub Actions -- 按 PR 变更文件触发增量检查:
name: Bandit Incremental
on:
pull_request:
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install bandit[toml]
- name: Run Bandit on changed files
run: |
CHANGED=$(git diff --name-only --diff-filter=ACMR origin/main -- '*.py')
if [ -n "$CHANGED" ]; then
echo "$CHANGED" | xargs bandit -c pyproject.toml
else
echo "No Python files changed."
fi
GitLab CI -- 按 MR 变更文件触发增量检查:
bandit-incremental:
image: python:3.12
before_script:
- pip install bandit[toml]
script:
- |
CHANGED=$(git diff --name-only --diff-filter=ACMR origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME -- '*.py')
if [ -n "$CHANGED" ]; then
echo "$CHANGED" | xargs bandit -c pyproject.toml
else
echo "No Python files changed."
fi
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
5.5 GitHub Action 插件
使用 PyCQA 官方提供的 bandit-action,支持自动上传 Code Scanning 结果:
# .github/workflows/bandit.yml
name: Bandit Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read
steps:
- uses: actions/checkout@v4
- name: Perform Bandit Analysis
uses: PyCQA/bandit-action@v1
with:
targets: "myapp/"
severity: "medium"
exclude: "tests/,migrations/"
6. 告警抑制(屏蔽)方法
6.1 通过 pre-commit 屏蔽
在 .pre-commit-config.yaml 中通过 exclude、files 等字段控制 Bandit 的检查范围:
repos:
- repo: https://github.com/PyCQA/bandit
rev: "1.9.4"
hooks:
- id: bandit
# 只检查 src 目录下的 Python 文件
files: '^src/.*\.py$'
# 排除测试和迁移文件
exclude: "^src/tests/.*|^.*/migrations/.*"
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]
6.2 通过工具配置文件屏蔽
在配置文件中通过 exclude_dirs/exclude 排除文件/目录,通过 skips 跳过指定规则:
TOML 格式(pyproject.toml):
[tool.bandit]
# 排除目录
exclude_dirs = ["tests", "migrations", "venv"]
# 跳过指定规则
skips = ["B101", "B601"]
# 只运行指定规则
# tests = ["B201", "B301"]
YAML 格式(bandit.yaml):
exclude_dirs: ["tests", "migrations", "venv"]
skips: ["B101", "B601"]
INI 格式(.bandit):
[bandit]
exclude = tests,migrations,venv
skips = B101,B601
6.3 通过工具命令行参数屏蔽
# 排除目录
bandit -r . --exclude "tests/,migrations/,venv/"
# 跳过指定规则
bandit -r . -s B101,B601
# 只运行指定规则
bandit -r . -t B201,B301
# 只报告指定严重级别及以上的问题
bandit -r . --severity-level=high
# 只报告指定置信级别及以上的问题
bandit -r . --confidence-level=high
# 组合使用
bandit -r . -s B101 --exclude "tests/" --severity-level=medium
参考:Bandit 快速入门
6.4 通过代码屏蔽
行级注释 # nosec:
在触发告警的行末添加 # nosec,Bandit 将跳过该行的所有安全检查:
# 跳过该行所有安全检查
self.process = subprocess.Popen('/bin/echo', shell=True) # nosec
行级注释指定规则 ID:
可以指定要跳过的具体规则 ID,其他安全问题仍会被报告:
# 只跳过 B602 和 B607
self.process = subprocess.Popen('/bin/ls *', shell=True) # nosec B602, B607
也可以使用完整的测试名称:
# 只跳过 assert_used(B101),B506 仍会被报告
assert yaml.load("{}") == [] # nosec assert_used
最佳实践:使用
# nosec时建议添加注释说明原因,避免后续引入新的安全问题被意外忽略。