ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从PDF到CI:安全性测试用例的可执行化与自动化回归实践

从PDF到CI:安全性测试用例的可执行化与自动化回归实践 简介这份《安全性测试用例》文档面向软件测试工程师、安全测试初学者及需要编写安全测试方案的项目人员聚焦Web系统安全性验证这一核心场景帮助读者建立从输入校验到访问控制的完整测试思路。资源包内含1个PDF文件大小约484KB内容以测试用例表格形式组织涵盖客户端与服务器端验证、URL非法入侵防范、日志记录完整性、密码策略与防恶意注册、未验证输入、访问控制、输入框验证、关键数据加密及认证请求方式等典型安全测试维度每条用例均给出Summary、Steps与Expected Results便于直接套用或二次改写。目前已有1674人学习下载适合作为安全测试用例库的参考模板也可用于日常回归测试与安全自查清单的整理帮助读者快速补齐Web安全测试的覆盖盲区。1. 安全性测试用例从一份“完整版”文档到可执行资产手里拿到一份名为“(完整版)安全性测试用例.doc.pdf”的文件大概是很多安全测试、渗透测试、QA 同学都遇到过的场景。它通常是一份几十页的表格列着“越权”“SQL 注入”“XSS”“文件上传”“敏感信息泄露”这些条目每条后面跟着“预期结果”和“实际结果”两栏。问题在于这份文档往往躺在共享盘里吃灰——测试时想不起来翻翻到了也不知道怎么把“验证越权”翻译成一条能跑的请求。安全性测试用例真正的价值不是“有这份文档”而是把它变成一套能被执行、能被回归、能被新人照着跑的工具。这篇笔记就围绕这个目标展开先讲清楚一份可用的安全性测试用例该长什么样再讲怎么把 PDF 里的条目拆成可执行的检查项最后落到自动化回归和持续维护上。适合手里已经有类似文档、但觉得它“不好用”的测试和开发同学。2. 安全性测试用例的骨架从条目到可执行检查项2.1 为什么大多数“完整版”用例其实不可执行翻过几份这类文档后会发现一个共性条目写的是“验证系统是否存在越权访问”但没写“用哪个账号、请求哪个接口、改哪个参数、看哪个响应字段”。这种描述对人来说是提示对执行来说是空白。可执行的安全性测试用例至少要包含五个要素前置条件账号、角色、数据状态、操作步骤具体请求或界面动作、输入数据参数值、文件、payload、预期结果状态码、响应体关键字、数据库变化、清理动作删掉测试数据、恢复配置。缺了任何一项用例就只能靠执行者的经验补全而经验是没法回归的。另一个常见问题是分类维度混乱。有的按 OWASP Top 10 分有的按功能模块分有的按“高危/中危/低危”分。分类本身没有对错但混在一起就会导致漏测——比如按模块分时跨模块的越权场景没人认领。我一般建议用“测试维度 × 业务模块”的二维矩阵来组织维度固定为身份认证、访问控制、输入校验、数据保护、会话管理、业务逻辑、文件处理这几类模块按实际系统切。这样每个格子里的用例都有明确的归属也方便按维度做回归。2.2 把 PDF 条目拆成五要素的实操方法拿到 PDF 后不要急着转 Word 或 Excel先做一轮“可执行性筛查”。具体做法是逐条读能直接补全五要素的留下补不全的标记为“需补充”完全无法执行的比如“验证系统是否安全”这种直接删掉或重写。下面是一个把原始条目拆成结构化用例的示例用 Python 字典表示方便后续导出成 Excel 或导入测试管理平台。# 原始 PDF 条目示意 # “越权访问验证普通用户不能访问管理员接口” # 拆解后的可执行用例 test_case { id: AC-001, dimension: 访问控制, # 测试维度 module: 用户管理, # 业务模块 title: 普通用户直接请求管理员用户列表接口, preconditions: { accounts: [user_a/普通用户, admin_b/管理员], data_state: user_a 已登录admin_b 存在但未使用 }, steps: [ 以 user_a 登录获取其会话 Cookie 或 Token, 构造 GET 请求/api/admin/users?page1size10, 在请求头中携带 user_a 的凭证, 发送请求并记录响应状态码与响应体 ], input: { method: GET, url: /api/admin/users, headers: {Cookie: user_a_session}, params: {page: 1, size: 10} }, expected: { status_code: 403, body_contains: [无权限, forbidden], body_not_contains: [admin_b, user_list] }, cleanup: 无需清理未产生数据变更 }这段代码的关键不是语法而是把“验证越权”这个模糊动作拆成了可编程的请求。preconditions里明确了用哪个账号、什么数据状态steps里写清了请求构造过程expected里用状态码和响应体关键字做断言而不是“应该被拒绝”这种主观判断。参数说明dimension和module用于分类检索input里的headers和params是实际发送时要替换的占位符expected里的body_not_contains用来防止“返回了数据但状态码是 200”这种隐蔽越权。2.3 用表格管理用例版本与覆盖度拆解完成后建议用一张主表来管理所有用例字段包括用例 ID、维度、模块、标题、优先级、自动化状态、最后执行时间、关联需求或缺陷号。这张表不需要多复杂但必须能回答两个问题某个模块的某个维度有没有用例某条用例最近一次执行是成功还是失败下面是一个简化的字段设计。字段名类型说明case_id字符串唯一标识如 AC-001dimension枚举身份认证/访问控制/输入校验/数据保护/会话管理/业务逻辑/文件处理module字符串业务模块名与需求文档对齐priority枚举P0/P1/P2P0 为每次回归必跑automatable布尔是否适合自动化接口类通常为真last_result枚举pass/fail/blocked/not_runlast_run日期最近一次执行日期defect_id字符串关联缺陷号无则留空这张表的价值在于当有人问“登录模块的会话管理测了吗”你能直接筛出module登录 and dimension会话管理的用例而不是翻 PDF。优先级字段决定了回归范围automatable字段决定了下一步投入自动化的方向。3. 把用例跑起来接口层安全测试的落地路径3.1 选型为什么接口层是安全性测试用例的主战场安全性测试用例的执行方式大致分三类手工界面操作、接口层脚本、代码层单元测试。手工操作适合探索性测试和复杂业务逻辑但没法回归代码层单元测试覆盖窄通常只测加密函数、权限判断函数这类纯逻辑。接口层脚本是性价比最高的它能覆盖大部分越权、注入、参数篡改、敏感信息泄露场景而且执行速度快、结果可断言、容易集成到 CI。常见做法是用 Python 的 requests 库或 Java 的 RestAssured 写脚本把用例表里的input和expected直接映射成代码。我一般会先挑 P0 且automatabletrue的用例通常能覆盖 60% 以上的高危场景。3.2 用 pytest 搭建可回归的安全用例集下面是一个用 pytest 组织安全用例的骨架包含登录获取凭证、发送请求、断言结果三个环节。代码里用 fixture 管理会话避免每条用例都重新登录。import pytest import requests BASE_URL http://target.example.com pytest.fixture(scopesession) def user_session(): 普通用户登录返回带凭证的 session s requests.Session() resp s.post(f{BASE_URL}/api/login, json{ username: user_a, password: Test1234 # 测试环境固定密码不要用生产密码 }) assert resp.status_code 200, 登录失败检查账号或接口地址 return s pytest.fixture(scopesession) def admin_session(): 管理员登录用于对比测试 s requests.Session() resp s.post(f{BASE_URL}/api/login, json{ username: admin_b, password: Admin1234 }) assert resp.status_code 200 return s def test_normal_user_cannot_access_admin_api(user_session): AC-001普通用户请求管理员接口应返回 403 resp user_session.get(f{BASE_URL}/api/admin/users, params{page: 1, size: 10}) assert resp.status_code 403, f期望 403实际 {resp.status_code} body resp.text.lower() assert admin_b not in body, 响应体中出现了管理员数据存在越权 assert user_list not in body, 响应体中出现了用户列表关键字 def test_sql_injection_in_search(user_session): IV-003搜索接口注入单引号不应报错或泄露数据 payload OR 11 resp user_session.get(f{BASE_URL}/api/search, params{keyword: payload}) assert resp.status_code in (200, 400), 非预期状态码 body resp.text.lower() assert sql not in body, 响应体包含 SQL 关键字可能泄露错误信息 assert syntax not in body, 响应体包含语法错误信息这段代码的逻辑说明user_session和admin_session两个 fixture 分别维护普通用户和管理员的会话scopesession表示整个测试过程只登录一次减少对登录接口的冲击。test_normal_user_cannot_access_admin_api对应前面的 AC-001 用例断言状态码为 403 且响应体不包含管理员数据。test_sql_injection_in_search演示了注入类用例的断言方式——不只看状态码还要检查响应体是否泄露数据库错误信息。参数说明BASE_URL换成实际测试环境地址密码用测试环境专用账号不要用生产凭证params里的 payload 可以根据实际接口调整但不要在生产环境执行。3.3 断言设计状态码之外还要看什么只断言状态码是安全测试用例最常见的翻车点。很多越权漏洞的表现是“返回 200 但数据为空”或“返回 200 且数据被过滤了一部分”如果只断言 403 就会漏掉。我一般会加三层断言第一层状态码第二层响应体关键字正向关键字和负向关键字都要有第三层数据量对比比如管理员接口返回 10 条普通用户请求同一接口如果返回 0 条但状态码 200也要标记为可疑。对于敏感信息泄露类用例还要检查响应头里有没有多余的Server、X-Powered-By字段以及响应体里有没有手机号、身份证号、邮箱等正则可匹配的内容。这些断言写进代码后用例才真正具备回归能力。4. 避坑与排查安全性测试用例落地时的五个血泪教训4.1 测试数据污染导致用例互相干扰现象越权用例偶尔通过偶尔失败排查发现是前一条用例创建的数据影响了后一条。原因多条用例共用同一个测试账号且没有清理动作。解决每条用例使用独立的数据前缀如test_ac001_并在cleanup阶段删除自己创建的数据或者用数据库事务回滚但接口测试通常做不到所以前缀隔离更实际。4.2 把“预期结果”写成主观描述现象用例执行后不同人对“应该被拒绝”的理解不一致有人觉得返回空列表也算拒绝有人觉得必须 403。原因预期结果没有量化。解决强制要求预期结果包含状态码、响应体关键字、数据量三个维度中的至少两个。比如“状态码 403 且响应体不包含用户列表”而不是“应该被拒绝”。4.3 忽略会话失效和 Token 过期场景现象会话管理维度的用例只测了“未登录不能访问”没测“登录后 Token 过期是否还能访问”。原因用例设计时只考虑了正常流程。解决在会话管理维度下强制包含三条用例——未携带凭证、携带过期凭证、携带已注销凭证。过期凭证可以通过修改系统时间或使用短有效期 Token 来构造。4.4 自动化用例直接跑在生产环境现象注入类用例在生产环境执行触发了 WAF 告警甚至影响了真实数据。原因没有区分环境BASE_URL写死。解决用环境变量或配置文件区分测试环境和生产环境自动化用例只允许在测试环境执行。如果必须在生产做验证只跑只读类用例且提前和运维报备。4.5 用例更新滞后于接口变更现象接口路径从/api/user/list改成/api/v2/users用例还在请求旧地址全部返回 404但没人发现。原因用例和接口定义没有关联机制。解决在用例表里增加“关联接口文档链接”字段接口变更时由开发或产品通知测试同时在 CI 里加一条“接口可达性检查”如果核心接口返回 404 就阻断回归。5. 让用例集持续可用从一次性文档到回归资产5.1 用 CI 定时跑 P0 用例并输出报告安全性测试用例如果只靠手工触发迟早会变成“上次跑是三个月前”。我一般会把 P0 用例接入 CI每天凌晨跑一次输出 JUnit 格式的报告失败时发通知。下面是一个 GitHub Actions 的配置片段核心是把 pytest 的执行结果转成报告并归档。name: security-regression on: schedule: - cron: 0 2 * * * # 每天凌晨 2 点执行 workflow_dispatch: # 支持手动触发 jobs: security-test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Set up Python uses: actions/setup-pythonv5 with: python-version: 3.11 - name: Install dependencies run: pip install -r requirements.txt - name: Run security tests env: BASE_URL: ${{ secrets.TEST_BASE_URL }} run: pytest tests/security -m p0 --junitxmlreport.xml - name: Upload report if: always() uses: actions/upload-artifactv4 with: name: security-report path: report.xml这段配置的关键点cron表达式控制执行频率workflow_dispatch允许手动补跑BASE_URL从 secrets 读取避免硬编码-m p0只跑标记为 P0 的用例控制执行时间if: always()保证即使测试失败也上传报告。参数说明cron的时区是 UTC需要按本地时间换算secrets.TEST_BASE_URL要在仓库设置里配置报告文件report.xml可以被 Jenkins、GitLab CI 等平台解析。5.2 用覆盖度矩阵决定下一轮补哪些用例用例集不是越全越好而是覆盖度矩阵里空格越少越好。我习惯每季度做一次覆盖度盘点横轴是业务模块纵轴是测试维度每个格子里填该模块该维度的用例数。空格子就是下一轮要补的方向。比如“支付模块 × 文件处理”是空的那就补文件上传相关的用例“用户模块 × 业务逻辑”只有一条那就补批量注册、频繁登录、验证码绕过等场景。这个矩阵不需要工具一张 Excel 就够关键是定期看、定期补。5.3 一个我坚持了多年的习惯每次修复一个安全缺陷后第一件事不是关掉缺陷单而是问一句现有用例为什么没覆盖到然后补一条用例把它标记为 P0并关联缺陷号。这样用例集才会随着系统一起生长而不是永远停留在最初那份 PDF 的水平。安全性测试用例的价值不在于文档有多厚而在于下一次同样的问题出现时能在 CI 里被自动拦住。希望帮到你。本文还有配套的精品资源点击获取
返回列表