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:一行命令搞定
验证安装:
输出示例:
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:
>> 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
>> 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
>> 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
>> 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
>> 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
>> 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
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验证:
输出应该显示0个问题:
修复一个漏洞,堵住一个后门。
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分钟配置,换来的是代码安全的长期保障。
声明:本站部分稿件版权来源于网络,如有侵犯版权,请及时联系我们。