Postman自动化安全测试实践:从API调试到安全左移
发布时间:2026/8/12 14:22:02 作者:尧图编辑部 阅读量:1,286

1. 项目概述为什么我们需要在Postman里做安全测试很多做接口测试的朋友可能对Postman的印象还停留在“一个很好用的API调试工具”上。点点按钮看看返回的JSON数据对不对断言一下状态码是不是200这就算是完成任务了。我以前也是这么想的直到在一次项目上线前的压力测试中我们一个看似“人畜无害”的查询接口被安全团队用几个简单的请求就拖垮了服务甚至还暴露了部分用户手机号。那次事故让我彻底明白功能正常不等于安全可靠。Postman作为一个几乎天天都在用的工具如果只用来做功能验证实在是有点“大材小用”了。实际上Postman的自动化测试能力完全可以被扩展成一套轻量级、可集成、可重复执行的安全性测试方案。它不是什么重量级的安全扫描器但恰恰是这种“轻量”和“贴近开发流程”的特性让它能在日常迭代中由开发或测试人员自己快速地对接口进行一轮基础的安全“体检”。想想看与其等到上线前让安全团队来一轮“大扫荡”不如在每次开发完接口、写完自动化测试用例之后顺手加上几个安全测试用例。这就像你写完代码会跑一下单元测试一样应该成为一种肌肉记忆。所以这个“Postman自动化测试Postman安全性测试实践”的核心就是把手头这个最熟悉的工具用到“刀刃”上。我们将不再满足于“接口通了”而是要追问“这个接口会不会被恶意参数搞垮”、“传输的数据有没有泄露敏感信息”、“身份认证的机制是不是足够健壮”。接下来我会把自己在多个项目中实践、踩坑后总结出来的一套方法从设计思路到具体操作毫无保留地分享出来。2. 安全测试的核心思路与Postman能力匹配在开始动手写脚本之前我们得先想清楚用Postman做安全测试到底要测什么以及Postman能帮我们测什么盲目地堆砌测试用例只会浪费时间找准发力点才能事半功倍。2.1 明确测试范畴四类基础安全风险基于OWASP API Security Top 10等权威指南和日常项目经验我们可以将Postman适合覆盖的安全测试范畴聚焦在以下四类这基本涵盖了API最常见的中低风险漏洞输入验证与注入类这是“万恶之源”。测试接口是否对用户输入进行了充分的清理和验证。典型测试包括SQL注入在参数中尝试‘ OR ‘1’’1、‘; DROP TABLE users; --等Payload。NoSQL注入针对MongoDB等尝试{“$ne”: null}、{“$gt”: “”}等操作符。命令注入在涉及系统调用的参数中尝试; ls、| cat /etc/passwd等。路径遍历在文件路径参数中尝试../../../etc/passwd。跨站脚本XSS在返回HTML或JSONP的接口中测试是否未对输出进行编码尝试scriptalert(1)/script。身份认证与授权类测试“你是谁”和“你能干什么”的机制是否牢固。令牌安全性测试JWT令牌是否缺少签名验证、是否使用弱密钥、过期时间是否合理。权限绕过使用低权限用户如普通用户的令牌去访问高权限如管理员的接口验证服务端是否做了严格的权限校验垂直越权。或者尝试修改请求中的资源ID如/api/user/123/profile改为/api/user/456/profile访问其他用户的资源水平越权。认证缺失直接访问本应需要认证的接口看是否会返回401/403。敏感数据暴露类检查接口响应是否“话太多”泄露了不该泄露的信息。信息泄露检查响应体中是否包含内部服务器错误信息、堆栈跟踪、数据库字段名、服务器版本、其他用户的个人身份信息PII如手机号、邮箱、身份证号等。过度数据暴露一个查询“我的订单”的接口是否把订单关联的所有用户隐私字段都返回了应该只返回必要字段。业务逻辑与资源滥用类这部分更贴近业务本身测试逻辑缺陷。批量赋值通过修改请求体尝试给本不应由用户更新的字段如isAdmin,balance赋值。资源耗尽尝试进行大量分页查询如limit10000、发起高频请求配合Postman的Collection Runner或Monitors测试服务端的限流策略是否生效。业务流程绕过例如不支付直接调用订单发货接口或者重复提交同一优惠券。2.2 Postman如何支撑这些测试Postman本身并不是一个安全漏洞扫描器但它提供了强大的“编程”和“自动化”能力让我们可以模拟攻击者的行为变量与环境用于管理不同角色的认证令牌如admin_token,user_token、测试主机地址等方便切换场景。Pre-request Scripts在请求发送前执行的JavaScript脚本。这是我们的“武器组装车间”。可以在这里动态生成恶意Payload如构造一个超长的字符串测试缓冲区溢出。篡改请求参数如自动递增ID进行水平越权测试。从环境变量中读取并设置攻击用的令牌。Tests Scripts在收到响应后执行的JavaScript脚本。这是我们的“伤害评估室”。可以在这里编写断言不仅检查状态码为200更要检查响应时间是否异常可能暗示DoS、响应体是否包含敏感关键词如“password”、“Exception trace”。解析响应提取数据如从错误信息中提取数据库结构用于后续攻击。将测试结果成功/失败输出到Postman的测试结果面板并给出明确提示。Collection Runner 与 Newman用于批量、自动化执行整个测试集合Collection。这是实现“自动化”的关键。你可以将安全测试用例和功能测试用例放在同一个Collection的不同文件夹每次回归测试时一并执行。Newman是命令行工具可以集成到CI/CD流水线如Jenkins, GitLab CI中让安全测试成为交付流水线的一环。Monitors定时运行Collection可用于监控生产或预发环境接口的稳定性同时也能发现因配置变更导致的安全防护失效问题。理解了“测什么”和“用什么测”我们就可以开始搭建我们的安全测试武器库了。3. 构建Postman安全测试集合从环境配置到用例设计这一部分我们进入实战环节。我会带你一步步创建一个专门用于安全测试的Postman Collection并分享我在组织用例和编写脚本时的具体心得。3.1 环境与Collection结构设计我的建议是为安全测试单独创建一个Collection而不是和功能测试用例混在一起。这样结构更清晰也方便在CI/CD中独立执行或按需执行。这个Collection的结构可以这样规划API安全测试套件 (Collection) ├── 环境变量文件 (Global, Environment) ├── 0. 工具函数与配置 (Folder) │ ├ [Pre-request] 通用脚本Payload生成器、令牌处理等 │ └ [Test] 通用断言敏感信息检测、错误信息检测 ├── 1. 身份认证与授权测试 (Folder) │ ├── 1.1 令牌安全性测试 │ ├── 1.2 水平越权测试 (用户A访问用户B资源) │ └── 1.3 垂直越权测试 (普通用户访问管理员接口) ├── 2. 输入验证测试 (Folder) │ ├── 2.1 SQL注入测试 (GET/POST参数) │ ├── 2.2 NoSQL注入测试 │ ├── 2.3 路径遍历测试 │ └── 2.4 XSS测试 (反射型/存储型) ├── 3. 敏感信息泄露测试 (Folder) │ ├── 3.1 错误信息泄露 │ └── 3.2 过度数据暴露 └── 4. 业务逻辑测试 (Folder) ├── 4.1 批量赋值测试 └── 4.2 限流策略测试环境变量配置创建两个环境比如Security_Test_Env和Security_Prod_Monitor_Env。里面需要包含base_url: 测试目标的基础地址。admin_token: 管理员用户的JWT或Token。user_token: 普通用户的JWT或Token。test_user_id: 用于测试的普通用户ID。another_user_id: 另一个用户ID用于水平越权测试。malicious_payloads: 可以是一个JSON数组的字符串存放各种注入Payload但更常见的做法是写在Pre-request脚本里或导入外部数据文件。实操心得不要把敏感Token直接硬编码在环境变量里尤其是生产环境的。Postman支持动态获取Token。可以专门写一个“获取Token”的请求放在Collection最前面在Pre-request Script里调用它将返回的Token自动设置到环境变量中。这样既安全又能保证Token不过期。3.2 编写可复用的Pre-request与Test脚本这是提升效率的关键。与其在每个请求里重复写相似的恶意Payload不如写成通用函数。在Collection层级的Pre-request Script中可以定义一些生成Payload的函数// 生成SQL注入测试Payload const generateSQLiPayloads () { return [ OR 11, ; DROP TABLE users; --, 1 ORDER BY 1--, 1 UNION SELECT null, username, password FROM users-- ]; }; // 生成路径遍历Payload const generatePathTraversalPayloads () { return [ ../../../etc/passwd, ..\\..\\..\\windows\\win.ini, %2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd // URL编码版本 ]; }; // 将函数挂载到全局pm对象上方便每个请求调用 pm.collectionVariables.set(generateSQLiPayloads, generateSQLiPayloads.toString()); // 注意实际中更优雅的做法是使用外部JS文件通过require引入但Postman沙盒环境限制较多。这里是一种变通。在Collection层级的Tests Script中可以定义一些通用的安全断言函数// 检查响应中是否泄露了敏感信息 pm.test(检查响应中无敏感信息泄露, function () { const responseBody pm.response.text(); const sensitiveKeywords [ password, credit_card, ssn, 身份证号, 手机号, token, secret_key, Exception, at line, SQL syntax, Database error, 堆栈跟踪 ]; sensitiveKeywords.forEach(keyword { pm.expect(responseBody).not.to.include(keyword, 响应体中疑似包含敏感关键词: ${keyword}); }); }); // 检查错误响应是否过于详细应返回通用错误而非堆栈信息 pm.test(错误响应未暴露堆栈信息, function () { if (pm.response.code 400) { const contentType pm.response.headers.get(Content-Type); if (contentType contentType.includes(application/json)) { const jsonData pm.response.json(); // 假设规范的错误响应格式是 {“code”: xxx, “message”: “...”} // 如果存在“stack”、“trace”等字段则可能泄露信息 pm.expect(jsonData).not.to.have.property(stack); pm.expect(jsonData).not.to.have.property(trace); } } });3.3 具体测试用例设计与实现示例我们以最经典的“水平越权”和“SQL注入”为例看看一个完整的测试请求在Postman里长什么样。用例1水平越权测试 - 用户A能否访问用户B的订单详情请求GET {{base_url}}/api/orders/{{another_user_order_id}}HeadersAuthorization: Bearer {{user_token}}(使用用户A的token)Pre-request Script:// 这个用例需要另一个用户的订单ID我们可以先调用一个接口获取或者从环境变量中读取一个已知ID。 // 这里假设我们已经通过其他方式将another_user_order_id设置到了环境变量中。 console.log(正在使用Token: ${pm.environment.get(user_token)} 访问订单: ${pm.environment.get(another_user_order_id)});Tests Script:// 测试1状态码应为403禁止访问而不是200成功或404未找到这可能泄露资源存在性 pm.test(水平越权访问应返回403, function () { pm.expect(pm.response.code).to.be.oneOf([403]); // 注意返回404在某些场景下也是可以接受的隐藏资源存在性但403是更明确的授权失败。 }); // 测试2响应体不应包含订单详情即使是错误信息也不应泄露他人数据 pm.test(响应体不包含他人订单数据, function () { const jsonData pm.response.json(); // 假设正常订单详情包含orderNumber字段 pm.expect(jsonData).not.to.have.property(orderNumber); }); // 调用通用检查 pm.test(通用安全检查, function () { // 这里可以调用之前定义在Collection层级的通用测试函数 // 由于Postman脚本作用域限制通常需要将通用检查代码复制过来或模块化引用 // 简便起见我们直接写检查逻辑 const body pm.response.text(); pm.expect(body).not.to.include(password); });用例2SQL注入测试 - 用户ID查询参数注入请求GET {{base_url}}/api/users?id{{malicious_id}}Pre-request Script:// 动态生成或从数组中选择一个SQL注入Payload const sqlPayloads [ 1 OR 11, 1 UNION SELECT null, username, password FROM users--, 1; SLEEP(5)-- // 基于时间的盲注测试 ]; // 随机选择一个Payload或者遍历测试需要结合Collection Runner const chosenPayload sqlPayloads[Math.floor(Math.random() * sqlPayloads.length)]; pm.environment.set(malicious_id, chosenPayload); console.log(本次测试使用的SQL注入Payload: ${chosenPayload});Tests Script:// 测试1应用程序不应返回数据库错误信息如MySQL PostgreSQL的错误 pm.test(无数据库错误信息泄露, function () { const body pm.response.text().toLowerCase(); const dbErrorIndicators [ sql syntax, mysql, postgresql, ora-, unclosed quotation mark, you have an error in your sql syntax ]; dbErrorIndicators.forEach(indicator { pm.expect(body).not.to.include(indicator); }); }); // 测试2响应时间不应异常用于检测基于时间的盲注是否成功 // 注意这个测试需要在基准响应时间的基础上进行比较理想的是在环境变量中设置一个baseline_response_time pm.test(响应时间无显著延迟防时间盲注, function () { const responseTime pm.response.responseTime; // 假设正常响应时间小于500ms这里阈值可以设高一些比如2秒 pm.expect(responseTime).to.be.below(2000); // 单位毫秒 console.log(请求响应时间: ${responseTime}ms); }); // 测试3返回的数据量不应异常增多例如注入‘1‘ OR ‘1‘‘1‘导致返回所有用户 // 这需要你知道正常请求返回的数据量比如条目数 // 假设正常查询一个用户返回的data数组长度为1 try { const jsonData pm.response.json(); if (jsonData.data Array.isArray(jsonData.data)) { pm.test(返回数据条目数正常防布尔盲注或数据泄露, function () { pm.expect(jsonData.data.length).to.be.at.most(5); // 假设最多不应超过5条 }); } } catch (e) { // 响应可能不是JSON忽略此测试 }注意事项进行SQL注入或任何破坏性测试时务必在测试环境进行并且最好使用专门搭建的、与生产隔离的测试数据库。避免测试数据污染或对测试环境造成不可逆的破坏。4. 自动化执行与CI/CD集成单个请求的手动测试意义有限安全测试的价值在于自动化、常态化的执行。Postman提供了两种强大的自动化工具Collection Runner和Newman。4.1 使用Collection Runner进行本地批量测试在Postman图形界面中打开你的“API安全测试套件”Collection点击“Run”按钮。选择环境在Runner界面选择你配置好的Security_Test_Env。迭代与延迟迭代次数对于需要遍历多个Payload的测试如用不同的SQL注入字符串跑同一个接口可以设置迭代次数并在Pre-request脚本中通过pm.iterationData来获取每次迭代的数据。更推荐的方式是使用数据文件。请求延迟建议设置一个延迟如500ms-1000ms避免对测试服务器造成瞬时洪水攻击也更能模拟真实攻击节奏。数据文件Data File这是实现参数化测试的利器。你可以创建一个CSV或JSON文件里面包含多行测试数据。示例CSV文件sqli_payloads.csv:test_case, payload basic_union, 1 UNION SELECT null, version()-- time_based, 1; SELECT SLEEP(5)-- error_based, 1 AND EXTRACTVALUE(1, CONCAT(0x7e, VERSION()))--在Runner中导入这个文件然后在请求的Pre-request Script中就可以通过pm.iterationData.get(“payload”)来获取当前迭代的Payload并设置到请求参数中。这样就能用一次运行测试所有Payload。查看结果运行结束后Postman会给出详细的报告哪个请求通过了安全测试绿色哪个请求发现了潜在问题红色断言失败。你需要仔细分析失败的用例判断是真正的漏洞还是误报比如业务逻辑允许的特定错误信息。4.2 使用Newman集成到CI/CD流水线这才是自动化的终极形态。Newman是Postman的命令行工具让你可以在服务器、在构建流水线中运行Collection。导出Collection与环境在Postman中将你的“API安全测试套件”Collection导出为JSON文件如api-security-suite.postman_collection.json。同样导出对应的环境变量文件如security-test-env.postman_environment.json。注意导出环境文件前请移除或替换其中的真实敏感Token建议在CI/CD中使用动态获取的方式。安装Newman确保CI/CD服务器如Jenkins Agent上安装了Node.js然后通过npm安装npm install -g newman。基础运行命令newman run api-security-suite.postman_collection.json \ -e security-test-env.postman_environment.json \ --reporters cli,json \ --reporter-json-export newman-report.json-e: 指定环境变量文件。--reporters: 指定报告格式cli在控制台输出json生成JSON格式报告。--reporter-json-export: 将JSON报告输出到指定文件。与Jenkins集成示例在Jenkins项目中添加一个执行Shell的构建步骤。#!/bin/bash # 假设Collection和环境文件都在项目根目录 # 1. 动态获取测试环境Token示例具体根据你的认证方式 API_TOKEN$(curl -s -X POST https://test-auth-server.com/login -d {user:ci_user,pass:$CI_PASSWORD} | jq -r .token) # 2. 更新环境变量文件或直接通过Newman命令传入 # 方式一使用sed临时修改环境文件如果Token在文件里 # sed -i s/\old_token_value\/\$API_TOKEN\/g security-test-env.postman_environment.json # 方式二使用全局变量推荐更安全 export ADMIN_TOKEN$API_TOKEN # 3. 运行Newman并通过--global-var传入变量 newman run api-security-suite.postman_collection.json \ -e security-test-env.postman_environment.json \ --global-var admin_token$ADMIN_TOKEN \ --reporters cli,junit \ --reporter-junit-export newman-report.xml \ --disable-unicode # 4. 根据Newman退出码判断测试结果0成功非0失败 if [ $? -ne 0 ]; then echo 安全测试失败发现潜在漏洞。 # 可以在这里触发通知如发送邮件、Slack消息 exit 1 # 使Jenkins构建标记为失败 else echo 安全测试通过。 fi这样每次代码合并或构建时都会自动执行这套安全测试用例。一旦有测试用例失败比如某个接口突然开始返回数据库错误CI/CD流水线就会中断及时阻止不安全的代码部署到更高级别的环境。实操心得在CI/CD中集成安全测试时一定要处理好测试数据。确保每次运行前测试数据库处于一个已知的、干净的状态。可以使用数据库迁移工具或专门的测试数据初始化脚本。否则测试结果会不可预测。另外对于“破坏性”测试如尝试DROP TABLE最好使用一个完全独立的、可以随时销毁重建的测试数据库实例。5. 高级技巧与常见问题排查掌握了基础方法后一些高级技巧和踩坑经验能让你事半功倍。5.1 处理动态数据与状态依赖安全测试经常需要依赖一些动态数据比如刚创建的用户ID、订单号。你不可能每次都手动去查。技巧使用“前置请求”获取测试数据。在你的安全测试Collection里可以第一个文件夹就叫“0. 测试数据准备”。里面放几个请求POST /api/test-users创建一个专门用于测试的普通用户在Tests脚本中将返回的userId和token保存到环境变量。POST /api/test-users/{{test_user_id}}/orders为该用户创建一个测试订单保存orderId。在后续的水平越权测试中就可以直接使用这些动态生成的ID了。技巧利用Postman的“setNextRequest”功能。在“测试数据准备”请求的Tests脚本里你可以用postman.setNextRequest(“水平越权测试请求名称”)来控制执行流确保数据准备完成后才执行真正的测试。这在Collection Runner中非常有用。5.2 测试HTTPS与证书问题问题测试环境可能使用自签名证书Newman运行时会报SSL证书错误。解决在Newman命令中添加--insecure参数来跳过SSL证书验证仅限测试环境newman run your-collection.json -e your-env.json --insecure5.3 性能考量与误报处理性能如果你的安全测试用例很多比如上百个注入Payload全部串行执行会非常慢。可以考虑将测试分类只对高风险接口如登录、订单、支付进行全套测试对普通查询接口只做基础测试。在Collection Runner或Newman中合理设置请求延迟。对于资源耗尽类测试如限流不要与其他测试同时进行避免干扰。误报这是自动化安全测试最常见的问题。比如“敏感信息泄露”断言失败因为业务逻辑确实需要返回手机号如用户个人资料页。“SQL注入”断言失败因为应用返回了自定义的业务错误信息其中包含了“SQL”这个词。处理方式精细化你的断言。不要一刀切。对于确实需要返回敏感信息的接口可以在该请求的Tests脚本中覆盖或禁用Collection级别的通用断言或者编写更精确的白名单断言例如只检查返回的字段中是否包含非本用户的手机号。5.4 测试结果分析与报告Newman报告除了命令行输出可以使用--reporters html生成漂亮的HTML报告更直观。也可以集成到Jenkins的JUnit插件中将newman-report.xml作为测试结果呈现。核心不是通过率而是失败分析安全测试的目的不是追求100%通过。恰恰相反你需要重点关注那些失败的用例。每一个失败的断言都是一个需要深入分析的“线索”。是漏洞是误报还是应用程序的防御机制起了作用比如返回了一个通用的“参数错误”建立一个分析流程将确认为漏洞的案例提交给开发团队修复将误报的案例更新测试脚本以避免再次干扰。6. 安全测试的局限性与互补方案必须清醒认识到Postman自动化测试只是应用安全左移实践中的一环它有明显的局限性黑盒测试它只能测试接口暴露出来的行为无法覆盖代码层面的逻辑漏洞如条件竞争、不安全的直接对象引用IDOR的深层逻辑。无法替代专业工具对于复杂的漏洞如服务器端请求伪造SSRF、XML外部实体注入XXE、不安全的反序列化等Postman测试脚本构造起来复杂且覆盖不全。这些需要依赖Burp Suite、OWASP ZAP等专业动态应用安全测试DAST工具或像SonarQube、Checkmarx这样的静态代码分析SAST工具。无法测试依赖库漏洞它无法发现项目使用的第三方库中存在的已知漏洞CVE。因此一个完整的安全体系应该是分层的开发阶段使用SAST工具扫描代码进行代码安全评审。集成测试阶段使用Postman进行API级别的自动化安全冒烟测试即本文所述。测试/预发阶段使用专业的DAST工具进行深度扫描进行手动渗透测试。生产阶段使用运行时应用自我保护RASP、Web应用防火墙WAF进行防护和监控。把Postman自动化安全测试放在第2阶段它的定位就非常清晰了一种低成本、高频率、由开发测试人员自主执行的快速反馈机制。它能抓住那些显而易见的、常见的安全问题在漏洞产生的早期就将其消灭从而为后续更深入、更昂贵的安全测试节省时间和资源。我个人在实际推行这套流程时最大的体会是“习惯成自然”。一开始开发同事会觉得这是额外负担。但当我们把安全测试用例和功能测试用例放在同一个Collection并集成到他们的本地调试和CI流程中后他们发现跑一次测试就能同时验证功能和安全反而提高了效率。几次迭代下来当有新人提交的代码因为一个简单的SQL注入测试失败而阻塞了构建时整个团队对安全的认识和重视程度都会上一个台阶。这或许就是“安全左移”最实在的价值。