极狐GitLab 的安全工具执行 API 模糊测试,例如 API 模糊测试,通过向正在运行的应用实例发送请求来进行测试。我们的模糊测试引擎对请求进行了变异,以触发应用中可能存在的意外行为。API 模糊测试的速度取决于以下因素:
- 我们的工具每秒可以发送多少请求到您的应用
- 您的应用响应请求的速度
- 必须发送多少请求来测试应用
- 您的 API 由多少个操作组成
- 每个操作中有多少个字段(例如 JSON 体、头、查询字符串、cookies 等)
如果按照本性能指南的建议进行操作后,API 模糊测试任务仍然比预期时间长,请联系支持以获得进一步帮助。
诊断性能问题
解决性能问题的第一步是了解是什么导致测试时间比预期的慢。我们看到的一些常见问题是:
- API 模糊测试运行在低 vCPU 的 runner 上
- 应用部署在慢速或单 CPU 实例上,无法应对测试负载
- 应用包含影响整体测试速度的慢操作(> 1/2 秒)
- 应用包含返回大量数据的操作(> 500K+)
- 应用包含大量操作(> 40)
应用包含影响整体测试速度的慢操作(> 1/2 秒)
API 模糊测试任务输出包含关于我们测试速度、每个操作响应速度以及摘要信息的有用信息。让我们看一些示例输出,看看它如何用于追踪性能问题:
API Fuzzing: Loaded 10 operations from: assets/har-large-response/large_responses.har
API Fuzzing:
API Fuzzing: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
API Fuzzing: - Parameters: (Headers: 4, Query: 0, Body: 0)
API Fuzzing: - Request body size: 0 Bytes (0 bytes)
API Fuzzing:
API Fuzzing: Finished testing operation 'GET http://target:7777/api/large_response_json'.
API Fuzzing: - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
API Fuzzing: - Performed 767 requests
API Fuzzing: - Average response body size: 130 MB
API Fuzzing: - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
API Fuzzing: - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)
这个任务控制台输出片段首先告诉我们发现了多少个操作(10),接着通知测试已开始针对特定操作进行,并总结了操作已完成的情况。摘要是这个日志输出中最有趣的部分。在摘要中,我们可以看到 API 模糊测试用了 767 个请求来完全测试这个操作及其相关字段。我们还可以看到平均响应时间是 2 秒,完成时间是 14 分钟。
2 秒的平均响应时间是这个特定操作测试时间长的一个好的初步指标。此外,我们可以看到响应体大小相当大。这里的罪魁祸首是大体积的响应体,每次请求传输这么多数据需要占用大部分的 2 秒时间。
对于这个问题,团队可能会决定:
- 使用更多 vCPU 的 runner,因为这允许 API 模糊测试并行化工作。这有助于降低测试时间,但如果不转移到高 CPU 机器,由于操作测试时间长,测试时间仍然可能难以降至 10 分钟以下。虽然较大的 runners 成本更高,但如果任务执行更快,您也可以节省分钟数。
- 从 API 模糊测试中排除这个操作。虽然这是最简单的,但它也有安全测试覆盖范围不完整的缺点。
- 从功能分支 API 模糊测试中排除操作,但在默认分支测试中包含它。
- 将 API 模糊测试拆分为多个任务。
可能的解决方案是使用这些解决方案的组合,以达到可接受的测试时间,假设您的团队的要求在 5-7 分钟范围内。
解决性能问题
以下部分记录了解决 API 模糊测试性能问题的各种选项:
使用更大的 runner
通过使用极狐GitLab API 模糊测试的 更大的 runner,可以实现最简单的性能提升。此表显示了在 Java Spring Boot REST API 基准测试期间收集的统计数据。在此基准测试中,目标和 API 模糊测试共享单个 runner 实例。
Hosted runner on Linux tag | Requests per Second |
---|---|
saas-linux-small-amd64 (default) |
255 |
saas-linux-medium-amd64 |
400 |
从这张表中可以看到,增加 runner 大小和 vCPU 数量可以对测试速度/性能产生巨大影响。
下面是一个 API 模糊测试的示例任务定义,它添加了一个 tags
部分以使用 Linux 上的中型 SaaS runner。该任务扩展了通过 API 模糊测试模板包含的任务定义。
apifuzzer_fuzz:
tags:
- saas-linux-medium-amd64
在 gl-api-security-scanner.log
文件中,您可以搜索字符串 Starting work item processor
来检查报告的最大 DOP(并行度)。最大 DOP 应大于或等于分配给 runner 的 vCPU 数。如果无法识别问题,请打开支持票来协助。
示例日志条目:
17:00:01.084 [INF] <Peach.Web.Core.Services.WebRunnerMachine> Starting work item processor with 4 max DOP
排除慢操作
在一个或两个慢操作的情况下,团队可能决定跳过测试这些操作。排除操作是通过使用 FUZZAPI_EXCLUDE_PATHS
配置 变量来完成的,如本节所述。
在此示例中,我们有一个返回大量数据的操作。操作是 GET http://target:7777/api/large_response_json
。为了排除它,我们提供 FUZZAPI_EXCLUDE_PATHS
配置变量,其中包含操作 URL 的路径部分 /api/large_response_json
。
要验证操作是否被排除,请运行 API 模糊测试任务并查看任务控制台输出。它在测试结束时包含一个包含和排除的操作列表。
apifuzzer_fuzz:
variables:
FUZZAPI_EXCLUDE_PATHS: /api/large_response_json
{{< alert type=”warning” >}}
排除测试的操作可能会导致一些漏洞未被检测到。
{{< /alert >}}
将测试拆分为多个任务
通过使用 FUZZAPI_EXCLUDE_PATHS
和 FUZZAPI_EXCLUDE_URLS
,API 模糊测试支持将测试拆分为多个任务。当拆分测试时,一个好的模式是禁用 apifuzzer_fuzz
任务并用两个具有标识名称的任务替换它。在此示例中,我们有两个任务,每个任务都在测试 API 的一个版本,因此我们的名称反映了这一点。然而,这种技术可以应用于任何情况,不仅限于 API 的版本。
我们在 apifuzzer_v1
和 apifuzzer_v2
任务中使用的规则是从 API 模糊测试模板 中复制的。
# Disable the main apifuzzer_fuzz job
apifuzzer_fuzz:
rules:
- if: $CI_COMMIT_BRANCH
when: never
apifuzzer_v1:
extends: apifuzzer_fuzz
variables:
FUZZAPI_EXCLUDE_PATHS: /api/v1/**
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH == 'true' &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH == '1' &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
apifuzzer_v2:
variables:
FUZZAPI_EXCLUDE_PATHS: /api/v2/**
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
在功能分支中排除操作,但不在默认分支中
在一个或两个慢操作的情况下,团队可能决定跳过测试这些操作,或者从功能分支测试中排除它们,但在默认分支测试中包含它们。排除操作是通过使用 FUZZAPI_EXCLUDE_PATHS
配置 变量来完成的,如本节所述。
在此示例中,我们有一个返回大量数据的操作。操作是 GET http://target:7777/api/large_response_json
。为了排除它,我们提供 FUZZAPI_EXCLUDE_PATHS
配置变量,其中包含操作 URL 的路径部分 /api/large_response_json
。我们的配置禁用主要的 apifuzzer_fuzz
任务,并创建两个新任务 apifuzzer_main
和 apifuzzer_branch
。apifuzzer_branch
任务设置为排除长操作并仅在非默认分支(例如功能分支)上运行。apifuzzer_main
分支设置为仅在默认分支(在此示例中为 main
)上执行。apifuzzer_branch
任务运行速度更快,允许快速开发周期,而仅在默认分支构建上运行的 apifuzzer_main
任务则需要更长时间运行。
要验证操作是否被排除,请运行 API 模糊测试任务并查看任务控制台输出。它在测试结束时包含一个包含和排除的操作列表。
# Disable the main job so we can create two jobs with
# different names
apifuzzer_fuzz:
rules:
- if: $CI_COMMIT_BRANCH
when: never
# API Fuzzing for feature branch work, excludes /api/large_response_json
apifuzzer_branch:
extends: apifuzzer_fuzz
variables:
FUZZAPI_EXCLUDE_PATHS: /api/large_response_json
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: never
- if: $CI_COMMIT_BRANCH
# API Fuzzing for default branch (main in our case)
# Includes the long running operations
apifuzzer_main:
extends: apifuzzer_fuzz
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH