千锋教育-做有情怀、有良心、有品质的职业教育机构

测试代码安全扫描:使用Bandit检测Python测试代码漏洞_千锋教育

来源:千锋教育
发布时间:2026-07-03 18:42:48

千锋教育品牌logo

01 为什么测试代码是黑客的“后门”?

测试代码——开发者眼中最安全的角落,却是黑客眼里最甜美的果实。

2023年,某知名云服务商遭遇数据泄露,攻击者并非直接攻破生产环境,而是通过一个暴露的测试API端点,获取了数据库访问凭证。这个测试端点,本该在部署前就被清理干净。但测试代码中硬编码的密钥,成了整条防线上的裂缝。

数据不会说谎:根据Verizon《2023年数据泄露调查报告》,超过60%的安全事件与内部系统漏洞相关,其中测试环境暴露的问题占比逐年上升。测试代码中常见的风险点包括:

  • ►硬编码凭证:测试数据库密码、API密钥直接写在代码里
  • ►不安全的网络请求:使用HTTP而非HTTPS,或未验证SSL证书
  • ►敏感信息泄露:日志中打印密码、Token等敏感数据
  • ►权限越界:测试代码绕过认证和授权检查
  • ►注入漏洞:SQL注入、命令注入在测试场景中屡见不鲜

测试代码的漏洞不是bug,是给黑客留的钥匙。

你的测试代码可能运行在Jenkins、GitLab CI等持续集成环境中。这些环境往往拥有访问生产数据库、云服务凭证的权限。一旦测试代码被攻破,攻击者就能通过这个“后门”直取核心资产。

02 Bandit:专为Python代码打造的安全扫描器

Bandit是Python生态中最成熟的安全扫描工具之一。它不是一个通用安全工具,而是专门针对Python代码的安全风险分析器。

Bandit的工作原理:它不是一个简单的字符串匹配工具。Bandit对Python代码进行AST(抽象语法树)解析,识别出潜在的安全风险模式。这意味着它能理解代码结构,减少误报率。

核心能力:

  • ►检测硬编码密码(通过模式匹配和熵值分析)
  • ►发现不安全的函数调用(如eval()、exec()、pickle.loads())
  • ►识别SQL注入风险(检测字符串拼接的SQL查询)
  • ►标记不安全的网络请求(如未验证SSL的requests.get())
  • ►检测文件权限问题(如chmod设置不当)

安装Bandit:一行命令搞定

bash
pip install bandit

验证安装:

bash
bandit --version

输出示例:

text
bandit 1.7.6
  python version: 3.11.4

为什么选择Bandit而不是其他工具?

  • ►零配置即可运行:bandit -r your_code/
  • ►可自定义规则:通过配置文件增减检测项
  • ►支持多种输出格式:JSON、XML、HTML
  • ►与CI/CD无缝集成:非零退出码可阻断构建

Bandit不是万能药,但它是你代码安全的守门人。

03 实战:三步完成测试代码安全扫描

第一步:创建测试代码样本

创建一个包含常见安全漏洞的测试文件test_vulnerable.py:

test_vulnerable.py - 一个包含安全漏洞的测试文件

import os

import pickle

import hashlib

import requests

import sqlite3

漏洞1:硬编码密码

DB_PASSWORD = "SuperSecret123!"

def test_database_connection():

"""测试数据库连接"""

conn = sqlite3.connect('test.db')

cursor = conn.cursor()

漏洞2:SQL注入风险 - 直接拼接用户输入

user_input = "admin' OR '1'='1"

query = f"SELECT * FROM users WHERE username = '{user_input}'"

cursor.execute(query)

漏洞3:不安全的反序列化

data = pickle.loads(b"malicious_payload")

漏洞4:不安全的哈希算法

password = "my_password"

hashed = hashlib.md5(password.encode()).hexdigest()

漏洞5:不安全的HTTP请求(无SSL验证)

response = requests.get('http://api.example.com/data', verify=False)

漏洞6:使用eval执行动态代码

user_code = "print('Hello')"

eval(user_code)

conn.close()

text

<div style="margin:24px 0;border:1px solid #E3F2FD;border-radius:8px;background:#FAFAFA;overflow:hidden;"><div style="display:flex;align-items:center;gap:12px;padding:12px 20px;background:linear-gradient(135deg,#E3F2FD,#BBDEFB);"><span style="width:36px;height:36px;background:#2196F3;color:#fff;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-weight:bold;font-size:16px;flex-shrink:0;">第二步:</span><span style="font-size:17px;font-weight:700;color:#1565C0;">运行Bandit扫描</span></div><div style="padding:16px 20px;"><p>执行基础扫描命令:</p>
<div style="margin:16px 0;border-radius:8px;overflow:hidden;border:1px solid #E0E0E0;"><div style="background:#263238;color:#B0BEC5;padding:6px 16px;font-size:12px;font-family:Consolas,monospace;"><span style="text-transform:uppercase;letter-spacing:1px;">bash</span></div><pre style="background:#1E272E;color:#E0E0E0;padding:16px 20px;margin:0;overflow-x:auto;font-size:14px;line-height:1.7;font-family:Consolas,"Courier New",monospace;"><code>bandit -r .</code></pre></div>
<p>输出示例:</p>
<div style="margin:16px 0;border-radius:8px;overflow:hidden;border:1px solid #E0E0E0;"><div style="background:#263238;color:#B0BEC5;padding:6px 16px;font-size:12px;font-family:Consolas,monospace;"><span style="text-transform:uppercase;letter-spacing:1px;">text</span></div><pre style="background:#1E272E;color:#E0E0E0;padding:16px 20px;margin:0;overflow-x:auto;font-size:14px;line-height:1.7;font-family:Consolas,"Courier New",monospace;"><code>[main]  INFO    profile include tests: None
[main]  INFO    profile exclude tests: None
[main]  INFO    cli include tests: None
[main]  INFO    cli exclude tests: None
[main]  INFO    running on Python 3.11.4

Run started:2024-01-15 14:30:00.123456

Test results:
&gt;&gt; Issue: [B105:hardcoded_password_string] Possible hardcoded password: 'SuperSecret123!'
   Severity: Medium   Confidence: Medium
   Location: test_vulnerable.py:5:1
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html

&gt;&gt; Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Medium
   Location: test_vulnerable.py:15:28
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html

&gt;&gt; Issue: [B301:pickle] Pickle and modules that wrap it can be unsafe when used to deserialize untrusted data
   Severity: Medium   Confidence: High
   Location: test_vulnerable.py:19:15
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b301_pickle.html

&gt;&gt; Issue: [B303:md5] Use of insecure MD2, MD4, MD5, or SHA1 hash function.
   Severity: Medium   Confidence: High
   Location: test_vulnerable.py:23:15
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b303_md5.html

&gt;&gt; Issue: [B501:request_with_no_cert_validation] Requests call with verify=False disabling SSL certificate checks
   Severity: Medium   Confidence: High
   Location: test_vulnerable.py:27:19
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b501_request_with_no_cert_validation.html

&gt;&gt; Issue: [B307:eval] Use of possibly insecure function - consider using safer alternatives
   Severity: Medium   Confidence: High
   Location: test_vulnerable.py:31:4
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b307_eval.html

--------------------------------------------------
6 issues found. (Sev: 0 High, 6 Medium, 0 Low)</code></pre></div>
<h3 id="第三步使用自定义配置优化扫描" style="color:#1976D2;margin-top:24px;">第三步:使用自定义配置优化扫描</h3>
<p>创建<code style="background:#F3E5F5;color:#6A1B9A;padding:1px 6px;border-radius:3px;font-size:0.9em;font-family:Consolas,monospace;">.bandit.yml</code>配置文件:</p></div></div>
# .bandit.yml
# 排除某些测试,减少误报
exclude_dirs:
  - ./tests/unit
  - ./docs

# 仅包含特定测试
tests:
  - B105  # 硬编码密码
  - B301  # 不安全的反序列化
  - B303  # 不安全的哈希
  - B307  # eval使用
  - B501  # 不安全的请求
  - B608  # SQL注入

# 设置严重级别阈值
skips:
  - B101  # 跳过assert语句检测(测试代码中常见)

# 自定义输出格式
output_format: json

使用配置文件运行:

bash
bandit -r . -c .bandit.yml -f json -o bandit_report.json

查看生成的JSON报告:

json
{
  "errors": [],
  "generated_at": "2024-01-15T14:35:00Z",
  "metrics": {
    "_totals": {
      "CONFIDENCE.HIGH": 5,
      "CONFIDENCE.LOW": 0,
      "CONFIDENCE.MEDIUM": 1,
      "SEVERITY.HIGH": 0,
      "SEVERITY.LOW": 0,
      "SEVERITY.MEDIUM": 6,
      "loc": 35,
      "nosec": 0
    }
  },
  "results": [
    {
      "code": "5 DB_PASSWORD = \"SuperSecret123!\"\n",
      "filename": "./test_vulnerable.py",
      "issue_confidence": "MEDIUM",
      "issue_severity": "MEDIUM",
      "issue_text": "Possible hardcoded password: 'SuperSecret123!'",
      "line_number": 5,
      "line_range": [5],
      "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html",
      "test_id": "B105"
    }
    // ... 更多结果
  ]
}

扫描不是终点,修复才是。

04 修复漏洞:从扫描到安全代码

Bandit发现问题只是第一步。真正的价值在于修复。以下是针对上述漏洞的修复方案:

修复1:硬编码密码 → 环境变量

问题代码:

python
DB_PASSWORD = "SuperSecret123!"

修复代码:

python
import os

DB_PASSWORD = os.environ.get('DB_PASSWORD')
if not DB_PASSWORD:
    raise ValueError("DB_PASSWORD environment variable not set")

修复2:SQL注入 → 参数化查询

问题代码:

python
query = f"SELECT * FROM users WHERE username = '{user_input}'"
cursor.execute(query)

修复代码:

python
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (user_input,))

修复3:不安全反序列化 → JSON

问题代码:

python
data = pickle.loads(b"malicious_payload")

修复代码:

python
import json

data = json.loads(b'{"safe": "payload"}')

修复4:弱哈希算法 → 强哈希

问题代码:

python
hashed = hashlib.md5(password.encode()).hexdigest()

修复代码:

python
import bcrypt

# 生成盐值并哈希
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password.encode(), salt)

修复5:不安全的HTTP请求 → 验证SSL

问题代码:

python
response = requests.get('http://api.example.com/data', verify=False)

修复代码:

python
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning

# 关闭警告(仅用于演示,生产环境必须验证)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# 使用HTTPS并验证证书
response = requests.get('https://api.example.com/data', verify=True)

修复6:eval执行 → 安全替代方案

问题代码:

python
eval(user_code)

修复代码:

python
import ast

# 使用AST解析代替eval
try:
    tree = ast.parse(user_code, mode='exec')
    # 仅允许安全操作
    for node in ast.walk(tree):
        if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
            if node.func.id not in ['print']:
                raise ValueError("Unsafe operation detected")
    exec(compile(tree, filename='<ast>', mode='exec'))
except SyntaxError:
    raise ValueError("Invalid Python code")

修复完成后,重新运行Bandit验证:

bash
bandit -r .

输出应该显示0个问题:

text
No issues found.

修复一个漏洞,堵住一个后门。

05 集成到CI/CD:让安全扫描自动化

手动扫描不够,将Bandit集成到持续集成流程中,实现自动化安全检测。

GitLab CI示例

创建.gitlab-ci.yml文件:

yaml
stages:
  - test
  - security

bandit-scan:
  stage: security
  image: python:3.11
  script:
    - pip install bandit
    - bandit -r . -f json -o bandit_report.json
    - |
      # 检查是否有高严重性问题
      if jq -e '.results[] | select(.issue_severity == "HIGH")' bandit_report.json > /dev/null; then
        echo "High severity issues found!"
        exit 1
      fi
  artifacts:
    paths:
      - bandit_report.json
    when: always
  only:
    - main
    - develop

GitHub Actions示例

创建.github/workflows/security-scan.yml:

yaml
name: Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  bandit-scan:
    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 Bandit
      run: pip install bandit
    
    - name: Run Bandit Scan
      run: |
        bandit -r . -f json -o bandit_report.json
        cat bandit_report.json
    
    - name: Upload Bandit Report
      uses: actions/upload-artifact@v3
      with:
        name: bandit-report
        path: bandit_report.json
    
    - name: Fail on High Severity
      run: |
        if jq -e '.results[] | select(.issue_severity == "HIGH")' bandit_report.json > /dev/null; then
          echo "High severity issues found!"
          exit 1
        fi

Jenkins Pipeline示例

groovy
pipeline {
    agent any
    
    stages {
        stage('Security Scan') {
            steps {
                script {
                    sh 'pip install bandit'
                    sh 'bandit -r . -f json -o bandit_report.json'
                    
                    def report = readJSON file: 'bandit_report.json'
                    def highIssues = report.results.findAll { it.issue_severity == 'HIGH' }
                    
                    if (highIssues.size() > 0) {
                        error "Found ${highIssues.size()} high severity issues"
                    }
                }
            }
            post {
                always {
                    archiveArtifacts artifacts: 'bandit_report.json'
                }
            }
        }
    }
}

---

安全不是一次性检查,而是持续的习惯。

你的测试代码现在有了安全守门人。每次提交,Bandit都会自动扫描,发现潜在风险。这不是锦上添花,而是雪中送炭。在黑客找到你测试代码的漏洞之前,先自己找到并修复它。

行动指令:今天就在你的测试项目中集成Bandit。3步操作,10分钟配置,换来的是代码安全的长期保障。

声明:本站部分稿件版权来源于网络,如有侵犯版权,请及时联系我们。

学习资源站

  • 免费全套视频教程
  • 企业实战项目源码
  • 大厂笔试真题题库
  • 行业前瞻发展趋势

相关推荐

  • 移动测试框架对比:XCTest vs Espresso vs UIAutomator_千锋教育 01移动测试三巨头:谁在统治你的App质量?移动端测试框架不是选择题,是生存题。2024年,全球移动应用数量突破800万,用户对App稳定性的容忍度降到3秒。一个Crash,直接卸载。XCTest、Espresso、UIAutomator,
  • 测试知识管理:使用Confluence构建测试知识库_千锋教育 01为什么你的测试团队总在“失忆”测试团队有一个隐形杀手:知识流失。项目迭代快,人员流动大,bug复现步骤、环境配置、测试数据、历史决策——这些信息每天都在蒸发。一个测试工程师离职,他脑子里装的100个坑位经验,公司一个都留不住。知识不被管
  • 测试环境隔离:使用Kubernetes Namespace隔离测试环境_千锋教育 01为什么你的测试环境总在“打架”?Namespace是解药你的测试环境是不是经常出现这种情况:开发A说“我的功能在测试环境跑不通”,运维B说“环境配置没问题”,最后发现是测试数据被另一个团队污染了。测试环境混乱,是软件测试效率的头号杀手。
  • 测试代码安全扫描:使用Bandit检测Python测试代码漏洞_千锋教育 01为什么测试代码是黑客的“后门”?测试代码——开发者眼中最安全的角落,却是黑客眼里最甜美的果实。2023年,某知名云服务商遭遇数据泄露,攻击者并非直接攻破生产环境,而是通过一个暴露的测试API端点,获取了数据库访问凭证。这个测试端点,本该
  • 测试报告分析:使用ELK堆栈分析测试结果_千锋教育 01为什么你的测试报告在“装死”?测试团队每天生成海量报告。Bandit跑一次Python代码扫描,输出几百行JSON。SonarQube分析完,又堆出一堆XML。人工打开这些文件,眼睛扫过密密麻麻的“Low”、“Medium”、“High
  • API契约测试:使用Spring Cloud Contract_千锋教育 01为什么你还在手动验证API兼容?因为你的测试数据在睡觉绝大多数团队做服务间API验证的方式:开发写完代码,跑个单元测试,然后丢给QA手动测。结果呢?上线前发现A服务改了接口,B服务还在用老字段。回滚、修复、加班。这不是技术问题,是流程管