API 安全测试作业故障排除
API 安全测试作业在 N 小时后超时
对于较大的仓库,API 安全测试作业可能会在 Linux 上的小型托管运行器上超时,这是默认设置。如果您的作业出现这种情况,应该升级到更大的运行器。
请参考以下文档章节获取帮助:
API 安全测试作业完成时间过长
请参阅性能调优和测试速度
错误:等待 DAST API 'http://127.0.0.1:5000' 可用时出错
在 v1.6.196 之前的 API 安全测试分析器版本中存在一个 bug,可能导致后台进程在某些条件下失败。解决方案是更新到更新版本的 API 安全测试分析器。
版本信息可以在 dast_api 作业的作业详情中找到。
如果问题出现在 v1.6.196 或更高版本中,请联系支持团队并提供以下信息:
- 引用本故障排除章节,并请求将问题升级到动态分析团队。
- 作业的完整控制台输出。
- 作为作业工件的
gl-api-security-scanner.log文件。在作业详情页面的右侧面板中,选择 Browse 按钮。 - 您
.gitlab-ci.yml文件中的dast_api作业定义。
错误消息
- 在 GitLab 15.6 及更高版本中,
等待 DAST API 'http://127.0.0.1:5000' 可用时出错 - 在 GitLab 15.5 及更早版本中,
等待 API Security 'http://127.0.0.1:5000' 可用时出错。
无法启动扫描器会话(未找到版本头)
当 API 安全测试引擎无法与扫描器应用程序组件建立连接时,会输出错误消息。该错误消息显示在 dast_api 作业的作业输出窗口中。此问题的常见原因是更改了 APISEC_API 变量的默认值。
错误消息
无法启动扫描器会话(未找到版本头)。
解决方案
- 从
.gitlab-ci.yml文件中删除APISEC_API变量。该值继承自 API 安全测试 CI/CD 模板。我们推荐此方法而不是手动设置值。 - 如果无法删除该变量,请检查该值是否在最新版本的 API 安全测试 CI/CD 模板中已更改。如果是,请在
.gitlab-ci.yml文件中更新该值。
无法启动与扫描器的会话。请重试,如果问题持续,请联系支持。
当 API 安全测试引擎无法与扫描器应用程序组件建立连接时,会输出错误消息。该错误消息显示在 dast_api 作业的作业输出窗口中。此问题的常见原因是后台组件无法使用所选端口,因为该端口已被占用。如果时序因素导致(竞争条件),此错误可能间歇性发生。当其他服务被映射到容器中导致端口冲突时,此问题最常出现在 Kubernetes 环境中。
在继续解决方案之前,重要的是确认错误消息是由于端口已被占用而产生的。要确认这是原因:
-
转到作业控制台。
-
查找工件
gl-api-security-scanner.log。您可以通过选择 Download 下载所有工件然后搜索文件,或者直接选择 Browse 开始搜索。 -
在文本编辑器中打开文件
gl-api-security-scanner.log。 -
如果错误消息是由于端口已被占用而产生的,您应该在文件中看到类似以下的消息:
-
无法绑定到地址 http://127.0.0.1:5500: 地址已被占用。 -
在 GitLab 15.4 及更早版本中:
无法绑定到地址 http://[::]:5000: 地址已被占用。
-
前一条消息中的文本 http://[::]:5000 在您的情况下可能不同,例如可能是 http://[::]:5500 或 http://127.0.0.1:5500。只要错误消息的其余部分相同,就可以安全地假设端口已被占用。
如果您没有找到端口已被占用的证据,请检查其他故障排除章节,这些章节也处理作业控制台输出中显示的相同错误消息。如果没有更多选项,请通过适当的渠道获取支持或请求改进。
一旦确认问题是由于端口已被占用而产生的。那么,GitLab 15.5 及更高版本引入了配置变量 APISEC_API_PORT。此配置变量允许为扫描器后台组件设置固定端口号。
解决方案
- 确保您的
.gitlab-ci.yml文件定义了配置变量APISEC_API_PORT。 - 将
APISEC_API_PORT的值更新为任何大于 1024 的可用端口号。我们建议检查新值是否未被 GitLab 使用。有关 GitLab 使用的端口完整列表,请参阅包默认值
应用程序无法确定目标 API 的基础 URL
当 API 安全测试引擎在检查 OpenAPI 文档后无法确定目标 API 时,会输出错误消息。当目标 API 未在 .gitlab-ci.yml 文件中设置、在 environment_url.txt 文件中不可用,并且无法使用 OpenAPI 文档计算时,会显示此错误消息。
API 安全测试引擎在检查不同来源时尝试获取目标 API 时有一个优先顺序。首先,它尝试使用 APISEC_TARGET_URL。如果未设置环境变量,则 API 安全测试引擎尝试使用 environment_url.txt 文件。如果没有 environment_url.txt 文件,则 API 安全测试引擎使用 OpenAPI 文档内容和 APISEC_OPENAPI 中提供的 URL(如果提供了 URL)来尝试计算目标 API。
最适合的解决方案取决于您的目标 API 是否每次部署都会更改。在静态环境中,目标 API 每次部署都相同,在这种情况下,请参考静态环境解决方案。如果目标 API 每次部署都会更改,则应应用动态环境解决方案。
API 安全测试作业从操作中排除某些路径
如果您发现某些路径被排除在操作之外,请确保:
-
变量
DAST_API_EXCLUDE_URLS未配置为排除您想要测试的操作。 -
目标定义 JSON 文件中定义了
consumes数组并且具有有效类型。有关示例定义,请参阅示例项目目标定义文件。
静态环境解决方案
此解决方案适用于目标 API URL 不变(静态)的流水线。
添加环境变量
对于目标 API 保持不变的环境,我们建议您使用 APISEC_TARGET_URL 环境变量指定目标 URL。在您的 .gitlab-ci.yml 中,添加一个变量 APISEC_TARGET_URL。该变量必须设置为 API 测试目标的基础 URL。例如:
stages:
- dast
include:
- template: API-Security.gitlab-ci.yml
variables:
APISEC_TARGET_URL: http://test-deployment/
APISEC_OPENAPI: test-api-specification.json动态环境解决方案
在动态环境中,您的目标 API 每次不同的部署都会更改。在这种情况下,有多种可能的解决方案,我们建议在处理动态环境时使用 environment_url.txt 文件。
使用 environment_url.txt
为了支持目标 API URL 在每次流水线中发生变化的动态环境,API 安全测试引擎支持使用包含要使用的 URL 的 environment_url.txt 文件。此文件不检入仓库,而是在流水线期间由部署测试目标的作业创建,并作为工件收集,供流水线中的后续作业使用。创建 environment_url.txt 文件的作业必须在 API 安全测试引擎作业之前运行。
- 修改测试目标部署作业,在项目根目录中添加一个包含基础 URL 的
environment_url.txt文件。 - 修改测试目标部署作业,将
environment_url.txt作为工件收集。
示例:
deploy-test-target:
script:
# 执行部署步骤
# 创建 environment_url.txt(示例)
- echo http://${CI_PROJECT_ID}-${CI_ENVIRONMENT_SLUG}.example.org > environment_url.txt
artifacts:
paths:
- environment_url.txt使用无效架构的 OpenAPI
在某些情况下,文档是使用无效架构自动生成的,或者无法及时手动编辑。在这些场景中,API 安全测试可以通过设置变量 APISEC_OPENAPI_RELAXED_VALIDATION 执行宽松验证。我们建议提供完全符合规范的 OpenAPI 文档,以防止意外行为。
编辑不符合规范的 OpenAPI 文件
为了检测和纠正不符合 OpenAPI 规范的元素,我们建议使用编辑器。编辑器通常提供文档验证,并创建符合架构的 OpenAPI 文档的建议。建议的编辑器包括:
| 编辑器 | OpenAPI 2.0 | OpenAPI 3.0.x | OpenAPI 3.1.x |
|---|---|---|---|
| Stoplight Studio | YAML, JSON | YAML, JSON | YAML, JSON |
| Swagger Editor | YAML, JSON | YAML, JSON | YAML, JSON |
如果您的 OpenAPI 文档是手动生成的,请在编辑器中加载文档并修复任何不符合规范的元素。如果您的文档是自动生成的,请在编辑器中加载它以识别架构中的问题,然后转到应用程序并根据您使用的框架进行更正。
启用 OpenAPI 宽松验证
宽松验证适用于 OpenAPI 文档无法满足 OpenAPI 规范但仍具有足够内容供不同工具使用的情况。执行验证,但在文档架构方面不那么严格。
API 安全测试仍然可以尝试使用不完全符合 OpenAPI 规范的 OpenAPI 文档。要指示 API 安全测试执行宽松验证,将变量 APISEC_OPENAPI_RELAXED_VALIDATION 设置为任何值,例如:
stages:
- dast
include:
- template: API-Security.gitlab-ci.yml
variables:
APISEC_PROFILE: Quick
APISEC_TARGET_URL: http://test-deployment/
APISEC_OPENAPI: test-api-specification.json
APISEC_OPENAPI_RELAXED_VALIDATION: 'On'OpenAPI 文档中没有操作使用任何支持的媒体类型
API 安全测试使用 OpenAPI 文档中指定的媒体类型生成请求。如果没有支持的媒体类型无法创建请求,则会抛出错误。
错误消息
错误,OpenApi 文档中没有操作使用任何支持的媒体类型。请检查 'OpenAPI 规范' 以查看支持的媒体类型。
解决方案
- 查看 OpenAPI 规范 部分中支持的媒体类型。
- 编辑您的 OpenAPI 文档,允许至少一个操作接受任何支持的媒体类型。或者,可以在 OpenAPI 文档级别设置支持的媒体类型,并将其应用于所有操作。此步骤可能需要更改您的应用程序,以确保应用程序接受支持的媒体类型。
错误:无法建立 SSL 连接,请查看内部异常。
API 安全测试兼容广泛的 TLS 配置,包括过时的协议和密码。 尽管支持广泛,您可能会遇到连接错误,如下所示:
错误,尝试下载 `<URL>` 时出错:
从 Uri:' <URL>' 检索内容时出错。
错误:无法建立 SSL 连接,请查看内部异常。此错误发生是因为 API 安全测试无法与给定 URL 的服务器建立安全连接。
要解决此问题:
如果错误消息中的主机支持非 TLS 连接,请在您的配置中将 https:// 更改为 http://。
例如,如果以下配置出现错误:
stages:
- dast
include:
- template: API-Security.gitlab-ci.yml
variables:
APISEC_TARGET_URL: https://test-deployment/
APISEC_OPENAPI: https://specs/openapi.json将 APISEC_OPENAPI 的前缀从 https:// 更改为 http://:
stages:
- dast
include:
- template: API-Security.gitlab-ci.yml
variables:
APISEC_TARGET_URL: https://test-deployment/
APISEC_OPENAPI: http://specs/openapi.json如果您无法使用非 TLS 连接访问 URL,请联系支持团队寻求帮助。
您可以使用 testssl.sh 工具 加速调查。从具有 bash shell 并能够连接到受影响服务器的机器:
- 从 https://github.com/drwetter/testssl.sh/releases 下载最新的
zip或tar.gz文件并解压。 - 运行
./testssl.sh --log https://specs。 - 将日志文件附加到您的支持工单中。
ERROR: Job failed: failed to pull image
当从需要身份验证才能访问(非公开)的容器注册表中拉取镜像时,会出现此错误消息。
在作业控制台输出中,错误如下所示:
Running with gitlab-runner 15.6.0~beta.186.ga889181a (a889181a)
on blue-2.shared.runners-manager.gitlab.com/default XxUrkriX
Resolving secrets
00:00
Preparing the "docker+machine" executor
00:06
Using Docker executor with image registry.gitlab.com/security-products/api-security:2 ...
Starting service registry.example.com/my-target-app:latest ...
Pulling docker image registry.example.com/my-target-app:latest ...
WARNING: Failed to pull image with policy "always": Error response from daemon: Get https://registry.example.com/my-target-app/manifests/latest: unauthorized (manager.go:237:0s)
ERROR: Job failed: failed to pull image "registry.example.com/my-target-app:latest" with specified policies [always]: Error response from daemon: Get https://registry.example.com/my-target-app/manifests/latest: unauthorized (manager.go:237:0s)错误消息
- 在 GitLab 15.9 及更早版本中,
ERROR: Job failed: failed to pull image后跟Error response from daemon: Get IMAGE: unauthorized。
解决方案
身份验证凭证使用 从私有容器注册表访问镜像 文档部分中概述的方法提供。使用的方法取决于您的容器注册表提供者及其配置。如果您使用的是第三方提供的容器注册表,例如云提供商(Azure、Google Could (GCP)、AWS 等),请查看提供者的文档以了解如何向其容器注册表进行身份验证。
以下示例使用静态定义的凭据身份验证方法。在此示例中,容器注册表是 registry.example.com,镜像是 my-target-app:latest。
-
阅读如何确定您的
DOCKER_AUTH_CONFIG数据以了解如何计算DOCKER_AUTH_CONFIG的变量值。配置变量DOCKER_AUTH_CONFIG包含 Docker JSON 配置,以提供适当的身份验证信息。例如,要访问私有容器注册表:registry.example.com,凭据为abcdefghijklmn,Docker JSON 如下所示:{ "auths": { "registry.example.com": { "auth": "abcdefghijklmn" } } } -
将
DOCKER_AUTH_CONFIG添加为 CI/CD 变量。您不应该直接在.gitlab-ci.yml文件中添加配置变量,而应该创建项目CI/CD 变量。 -
重新运行您的作业,现在使用静态定义的凭据登录私有容器注册表
registry.example.com,并允许您拉取镜像my-target-app:latest。如果成功,作业控制台将显示如下输出:Running with gitlab-runner 15.6.0~beta.186.ga889181a (a889181a) on blue-4.shared.runners-manager.gitlab.com/default J2nyww-s Resolving secrets 00:00 Preparing the "docker+machine" executor 00:56 Using Docker executor with image registry.gitlab.com/security-products/api-security:2 ... Starting service registry.example.com/my-target-app:latest ... Authenticating with credentials from $DOCKER_AUTH_CONFIG Pulling docker image registry.example.com/my-target-app:latest ... Using docker image sha256:139c39668e5e4417f7d0eb0eeb74145ba862f4f3c24f7c6594ecb2f82dc4ad06 for registry.example.com/my-target-app:latest with digest registry.example.com/my-target- app@sha256:2b69fc7c3627dbd0ebaa17674c264fcd2f2ba21ed9552a472acf8b065d39039c ... Waiting for services to be up and running (timeout 30 seconds)...
连续扫描之间的漏洞结果不同
在没有代码或配置更改的情况下,连续扫描可能会返回不同的漏洞发现。这主要是由于目标环境及其状态的不确定性,以及扫描器发送的请求的并行化。扫描器并行发送多个请求以优化扫描时间,这意味着目标服务器响应请求的确切顺序是预先确定的。
如果服务器负载过载且无法在给定阈值内响应对测试的请求,可能会检测到基于请求和响应之间时间长度的时序攻击漏洞,例如操作系统命令或 SQL 注入。当服务器未负载过载时,相同的扫描执行可能不会返回这些漏洞的积极发现,从而导致结果不同。分析目标服务器,性能调优和测试速度,以及在测试期间建立最佳服务器性能的基线,可能有助于识别由于上述因素可能出现误报的位置。
sudo: 设置了"no new privileges"标志,这阻止了 sudo 以 root 身份运行。
从分析器的 v5 版本开始,默认使用非 root 用户。这需要在执行特权操作时使用 sudo。
此错误发生在特定的容器守护进程设置中,该设置阻止运行的容器获得新权限。在大多数设置中,这不是默认配置,而是专门配置的,通常作为安全加固指南的一部分。
错误消息
可以通过在执行 before_script 或 APISEC_PRE_SCRIPT 时生成的错误消息来识别此问题:
$ sudo apk add nodejs
sudo: 设置了"no new privileges"标志,这阻止了 sudo 以 root 身份运行。
sudo: 如果 sudo 在容器中运行,您可能需要调整容器配置以禁用该标志。解决方案
可以通过以下方式解决此问题:
-
以
root用户运行容器。您应该测试此配置,因为它可能不适用于所有情况。这可以通过修改 CI/CD 配置并检查作业输出来确保whoami返回root而不是gitlab来完成。如果显示gitlab,请使用其他解决方法。测试确认更改成功后,可以删除before_script。api_security: image: name: $SECURE_ANALYZERS_PREFIX/$APISEC_IMAGE:$APISEC_VERSION$APISEC_IMAGE_SUFFIX docker: user: root before_script: - whoami示例作业控制台输出:
Executing "step_script" stage of the job script Using docker image sha256:8b95f188b37d6b342dc740f68557771bb214fe520a5dc78a88c7a9cc6a0f9901 for registry.gitlab.com/security-products/api-security:5 with digest registry.gitlab.com/security-products/api-security@sha256:092909baa2b41db8a7e3584f91b982174772abdfe8ceafc97cf567c3de3179d1 ... $ whoami root $ /peach/analyzer-api-security 17:17:14 [INF] API Security: Gitlab API Security 17:17:14 [INF] API Security: ------------------- 17:17:14 [INF] API Security: 17:17:14 [INF] API Security: version: 5.7.0 -
包装容器并在构建时添加任何依赖项。此选项的好处是运行时权限低于 root,这可能是某些客户的要求。
-
创建一个新的
Dockerfile来包装现有镜像。ARG SECURE_ANALYZERS_PREFIX ARG APISEC_IMAGE ARG APISEC_VERSION ARG APISEC_IMAGE_SUFFIX FROM $SECURE_ANALYZERS_PREFIX/$APISEC_IMAGE:$APISEC_VERSION$APISEC_IMAGE_SUFFIX USER root RUN pip install ... RUN apk add ... USER gitlab -
在 API 安全测试作业开始之前,构建新镜像并将其推送到您的本地容器注册表。应在
api_security作业完成后删除该镜像。TARGET_NAME=apisec-$CI_COMMIT_SHA docker build -t $TARGET_IMAGE \ --build-arg "SECURE_ANALYZERS_PREFIX=$SECURE_ANALYZERS_PREFIX" \ --build-arg "APISEC_IMAGE=$APISEC_IMAGE" \ --build-arg "APISEC_VERSION=$APISEC_VERSION" \ --build-arg "APISEC_IMAGE_SUFFIX=$APISEC_IMAGE_SUFFIX" \ . docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY docker push $TARGET_IMAGE -
扩展
api_security作业并使用新镜像名称。api_security: image: apisec-$CI_COMMIT_SHA -
从注册表中删除临时容器。有关删除容器镜像的信息,请参阅此文档页面。
-
-
更改 GitLab Runner 配置,禁用 no-new-privileges 标志。这可能会带来安全影响,应与您的运维和安全团队讨论。
Index was outside the bounds of the array. at Peach.Web.Runner.Services.RunnerOptions.GetHeaders()
此错误消息表明 API 安全测试分析器无法解析 APISEC_REQUEST_HEADERS 或 APISEC_REQUEST_HEADERS_BASE64 配置变量的值。
错误消息
可以通过两个错误消息来识别此问题。第一个错误消息在作业控制台输出中看到,第二个在 gl-api-security-scanner.log 文件中。
来自作业控制台的错误消息:
05:48:38 [ERR] API Security: Testing failed: An unexpected exception occurred: Index was outside the bounds of the array.来自 gl_api_security-scanner.log 的错误消息:
08:45:43.616 [ERR] <Peach.Web.Core.Services.WebRunnerMachine> Unexpected exception in WebRunnerMachine::Run()
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Peach.Web.Runner.Services.RunnerOptions.GetHeaders() in /builds/gitlab-org/security-products/analyzers/api-fuzzing-src/web/PeachWeb/Runner/Services/[RunnerOptions.cs:line 362
at Peach.Web.Runner.Services.RunnerService.Start(Job job, IRunnerOptions options) in /builds/gitlab-org/security-products/analyzers/api-fuzzing-src/web/PeachWeb/Runner/Services/RunnerService.cs:line 67
at Peach.Web.Core.Services.WebRunnerMachine.Run(IRunnerOptions runnerOptions, CancellationToken token) in /builds/gitlab-org/security-products/analyzers/api-fuzzing-src/web/PeachWeb/Core/Services/WebRunnerMachine.cs:line 321
08:45:43.634 [WRN] <Peach.Web.Core.Services.WebRunnerMachine> * Session failed: An unexpected exception occurred: Index was outside the bounds of the array.
08:45:43.677 [INF] <Peach.Web.Core.Services.WebRunnerMachine> Finished testing. Performed a total of 0 requests.解决方案
此问题是由于格式错误的 APISEC_REQUEST_HEADERS 或 APISEC_REQUEST_HEADERS_BASE64 变量引起的。预期格式是一个或多个 Header: value 构造的标头,用逗号分隔。解决方案是更正语法以匹配预期格式。
有效示例:
Authorization: Bearer XYZX-Custom: Value,Authorization: Bearer XYZ
无效示例:
Header:,valueHeaderA: value,HeaderB:,HeaderC: valueHeader