Help us learn about your current experience with the documentation. Take the survey.

自定义分析器设置

认证

认证通过提供认证令牌作为头信息或 Cookie 来处理。你可以提供一个执行认证流程或计算令牌的脚本。

HTTP 基本认证

HTTP 基本认证 是一种内置于 HTTP 协议的认证方法,与 传输层安全(TLS) 结合使用。

我们建议你 创建一个 CI/CD 变量 用于密码(例如,TEST_API_PASSWORD),并将其设置为掩码。你可以在 GitLab 项目的页面中,通过 设置 > CI/CD,在 变量 部分创建 CI/CD 变量。由于 掩码变量的限制,你应该在添加变量前对密码进行 Base64 编码。

最后,在你的 .gitlab-ci.yml 文件中添加两个 CI/CD 变量:

  • APISEC_HTTP_USERNAME:认证的用户名。
  • APISEC_HTTP_PASSWORD_BASE64:认证用的 Base64 编码密码。
stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_HAR: test-api-recording.har
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_HTTP_USERNAME: testuser
  APISEC_HTTP_PASSWORD_BASE64: $TEST_API_PASSWORD

原始密码

如果你不想对密码进行 Base64 编码(或者你使用的是 GitLab 15.3 或更早版本),你可以提供原始密码 APISEC_HTTP_PASSWORD,而不是使用 APISEC_HTTP_PASSWORD_BASE64

令牌(Bearer tokens)

令牌(Bearer tokens)被多种不同的认证机制使用,包括 OAuth2 和 JSON Web Tokens(JWT)。令牌通过 Authorization HTTP 头传输。要在 API 安全测试中使用令牌,你需要以下之一:

  • 一个不会过期的令牌。
  • 一种能生成持续整个测试长度的令牌的方法。
  • 一个 Python 脚本,API 安全测试可以调用它来生成令牌。

令牌不过期

如果令牌不过期,使用 APISEC_OVERRIDES_ENV 变量提供它。该变量的内容是一个 JSON 片段,提供了要添加到 API 安全测试发出的 HTTP 请求中的头信息和 Cookie。

按照以下步骤使用 APISEC_OVERRIDES_ENV 提供令牌:

  1. 创建一个 CI/CD 变量,例如 TEST_API_BEARERAUTH,值为 {"headers":{"Authorization":"Bearer dXNlcm5hbWU6cGFzc3dvcmQ="}}(替换你的令牌)。你可以在 GitLab 项目页面中,通过 设置 > CI/CD,在 变量 部分创建 CI/CD 变量。 由于 TEST_API_BEARERAUTH 的格式,无法对该变量进行掩码。要掩码令牌值,你可以创建第二个包含令牌值的变量,并将 TEST_API_BEARERAUTH 定义为 {"headers":{"Authorization":"Bearer $MASKED_VARIABLE"}}

  2. 在你的 .gitlab-ci.yml 文件中,将 APISEC_OVERRIDES_ENV 设置为刚创建的变量:

    stages:
      - dast
    
    include:
      - template: API-Security.gitlab-ci.yml
    
    variables:
      APISEC_PROFILE: Quick
      APISEC_OPENAPI: test-api-specification.json
      APISEC_TARGET_URL: http://test-deployment/
      APISEC_OVERRIDES_ENV: $TEST_API_BEARERAUTH
  3. 要验证认证是否有效,运行 API 安全测试并查看作业日志和测试 API 的应用程序日志。

测试运行时生成的令牌

如果令牌必须在测试期间生成且不会过期,你可以向 API 安全测试提供一个包含令牌的文件。之前的阶段和作业,或 API 安全测试作业的一部分,可以生成此文件。

API 安全测试期望收到具有以下结构的 JSON 文件:

{
  "headers" : {
    "Authorization" : "Bearer dXNlcm5hbWU6cGFzc3dvcmQ="
  }
}

这个文件可以由之前的阶段生成,并通过 APISEC_OVERRIDES_FILE CI/CD 变量提供给 API 安全测试。

在你的 .gitlab-ci.yml 文件中设置 APISEC_OVERRIDES_FILE

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_OVERRIDES_FILE: dast-api-overrides.json

要验证认证是否有效,运行 API 安全测试并查看作业日志和测试 API 的应用程序日志。

Token 过期时间较短

如果Bearer token必须在扫描完成前生成并过期,你可以提供一个程序或脚本来让API安全测试扫描器在指定的时间间隔内执行。提供的脚本在一个安装了Python 3和Bash的Alpine Linux容器中运行。如果Python脚本需要额外的包,它必须检测到这一点并在运行时安装这些包。

脚本必须创建一个包含Bearer token的JSON文件,格式如下:

{
  "headers" : {
    "Authorization" : "Bearer dXNlcm5hbWU6cGFzc3dvcmQ="
  }
}

你必须提供三个CI/CD变量,每个都设置为正确的操作值:

  • APISEC_OVERRIDES_FILE:提供的命令生成的JSON文件。
  • APISEC_OVERRIDES_CMD:生成JSON文件的命令。
  • APISEC_OVERRIDES_INTERVAL:运行命令的时间间隔(秒)。

例如:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_OVERRIDES_FILE: dast-api-overrides.json
  APISEC_OVERRIDES_CMD: renew_token.py
  APISEC_OVERRIDES_INTERVAL: 300

要验证认证是否正常工作,运行API安全测试并查看作业日志和测试API的应用日志。有关覆盖命令的更多信息,请参阅[覆盖项部分]。

覆盖项

API安全测试提供了一种方法来添加或覆盖请求中的特定项目,例如:

  • Headers
  • Cookies
  • 查询字符串
  • 表单数据
  • JSON节点
  • XML节点

你可以使用它注入语义版本头信息、认证等。[认证部分]包含了使用覆盖项实现这一目的的示例。

覆盖项使用一个JSON文档,每种类型的覆盖项由一个JSON对象表示:

{
  "headers": {
    "header1": "value",
    "header2": "value"
  },
  "cookies": {
    "cookie1": "value",
    "cookie2": "value"
  },
  "query":      {
    "query-string1": "value",
    "query-string2": "value"
  },
  "body-form":  {
    "form-param1": "value",
    "form-param2": "value"
  },
  "body-json":  {
    "json-path1": "value",
    "json-path2": "value"
  },
  "body-xml" :  {
    "xpath1":    "value",
    "xpath2":    "value"
  }
}

设置单个Header的示例:

{
  "headers": {
    "Authorization": "Bearer dXNlcm5hbWU6cGFzc3dvcmQ="
  }
}

同时设置Header和Cookie的示例:

{
  "headers": {
    "Authorization": "Bearer dXNlcm5hbWU6cGFzc3dvcmQ="
  },
  "cookies": {
    "flags": "677"
  }
}

设置body-form覆盖项的示例用法:

{
  "body-form":  {
    "username": "john.doe"
  }
}

当请求体只有表单数据内容时,覆盖引擎会使用body-form

设置body-json覆盖项的示例用法:

{
  "body-json":  {
    "$.credentials.access-token": "iddqd!42.$"
  }
}

对象body-json中的每个JSON属性名都是一个JSON Path表达式。JSON Path表达式$.credentials.access-token标识要被值iddqd!42.$覆盖的节点。当请求体只有JSON内容时,覆盖引擎会使用body-json

例如,如果请求体设置为以下JSON:

{
    "credentials" : {
        "username" :"john.doe",
        "access-token" : "non-valid-password"
    }
}

它会变为:

{
    "credentials" : {
        "username" :"john.doe",
        "access-token" : "iddqd!42.$"
    }
}

以下是设置body-xml覆盖项的示例。第一个条目覆盖XML属性,第二个条目覆盖XML元素:

{
  "body-xml" :  {
    "/credentials/@isEnabled": "true",
    "/credentials/access-token/text()" : "iddqd!42.$"
  }
}

对象body-xml中的每个JSON属性名都是一个XPath v2表达式。XPath表达式/credentials/@isEnabled标识要用值true覆盖的属性节点。XPath表达式/credentials/access-token/text()标识要用值iddqd!42.$覆盖的元素节点。当请求体只有XML内容时,覆盖引擎会使用body-xml

例如,如果请求体设置为以下XML:

<credentials isEnabled="false">
  <username>john.doe</username>
  <access-token>non-valid-password</access-token>
</credentials>

它会变为:

<credentials isEnabled="true">
  <username>john.doe</username>
  <access-token>iddqd!42.$</access-token>
</credentials>

你可以将此JSON文档作为文件或环境变量提供。你也可以提供一个命令来生成JSON文档。该命令可以按间隔运行以支持过期的值。

使用文件

要将覆盖的JSON作为文件提供,需设置 APISEC_OVERRIDES_FILE CI/CD 变量。路径相对于作业当前工作目录。

以下是一个示例 .gitlab-ci.yml

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_OVERRIDES_FILE: dast-api-overrides.json

使用CI/CD变量

要将覆盖的JSON作为CI/CD变量提供,请使用 APISEC_OVERRIDES_ENV 变量。
这允许你将JSON放置在可被屏蔽和保护的变量中。

在此示例 .gitlab-ci.yml 中,APISEC_OVERRIDES_ENV 变量直接设置为JSON:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_OVERRIDES_ENV: '{"headers":{"X-API-Version":"2"}}'

在此示例 .gitlab-ci.yml 中,SECRET_OVERRIDES 变量提供了JSON。这是一个
在UI中定义的群组或实例CI/CD变量

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_OVERRIDES_ENV: $SECRET_OVERRIDES

使用命令

若值需要在到期时生成或重新生成,你可以提供一个程序或脚本供
API安全测试扫描器在指定间隔执行。提供的命令在已安装Python 3和Bash的Alpine Linux容器中运行。

你必须将环境变量 APISEC_OVERRIDES_CMD 设置为要执行的程序或脚本。提供的命令会创建之前定义的覆盖JSON文件。

你可能想安装其他脚本运行时(如NodeJS或Ruby),或需要为覆盖命令安装依赖项。此时应将 APISEC_PRE_SCRIPT 设为提供这些先决条件的脚本的文件路径。由 APISEC_PRE_SCRIPT 提供的脚本会在分析器启动前执行一次。

当执行需要提升权限的操作时,请使用 sudo 命令。
例如,sudo apk add nodejs

有关安装Alpine Linux软件包的信息,请参阅 Alpine Linux软件包管理 页面。

你必须提供三个CI/CD变量,每个都需正确设置以确保正常运行:

  • APISEC_OVERRIDES_FILE:由提供的命令生成的文件。
  • APISEC_OVERRIDES_CMD:负责定期生成覆盖JSON文件的命令。
  • APISEC_OVERRIDES_INTERVAL:运行命令的时间间隔(秒)。

可选:

  • APISEC_PRE_SCRIPT:在扫描开始前安装运行时或依赖项的脚本。

要在Alpine Linux中执行脚本,你必须先用 chmod 命令设置 执行权限。例如,要为所有人设置 script.py 的执行权限,请使用命令:sudo chmod a+x script.py。若需要,可将带有执行权限的 script.py 版本化。

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_OVERRIDES_FILE: dast-api-overrides.json
  APISEC_OVERRIDES_CMD: renew_token.py
  APISEC_OVERRIDES_INTERVAL: 300

调试重写

默认情况下,重写命令的输出是隐藏的。您可以选择将变量 APISEC_OVERRIDES_CMD_VERBOSE 设置为任意值,以将重写命令的输出记录到 gl-api-security-scanner.log 作业工件文件中。这在测试您的重写脚本时很有用,但之后应禁用它,因为它会减慢测试速度。

您还可以将脚本中的消息写入日志文件,该文件会在作业完成或失败时被收集。日志文件必须创建在特定位置并遵循命名约定。

向您的重写脚本添加基本日志记录非常有用,以防脚本在作业的标准运行过程中意外失败。日志文件会自动作为作业的工件包含在内,让您可以在作业完成后下载它。

按照我们的示例,我们在环境变量 APISEC_OVERRIDES_CMD 中提供了 renew_token.py。请注意脚本中的两点:

  • 日志文件保存在环境变量 CI_PROJECT_DIR 指示的位置。
  • 日志文件名应匹配 gl-*.log
#!/usr/bin/env python

# 重写命令示例

# 重写命令可以更新重写JSON文件

# 使用新值。这是更新将在测试期间过期的

# 身份验证令牌的好方法。

import logging
import json
import os
import requests
import backoff

# [1] 将日志文件存储在环境变量CI_PROJECT_DIR指示的目录中
working_directory = os.environ.get( 'CI_PROJECT_DIR')
overrides_file_name = os.environ.get('APISEC_OVERRIDES_FILE', 'dast-api-overrides.json')
overrides_file_path = os.path.join(working_directory, overrides_file_name)

# [2] 文件名应匹配模式:gl-*.log
log_file_path = os.path.join(working_directory, 'gl-user-overrides.log')

# 设置日志记录器
logging.basicConfig(filename=log_file_path, level=logging.DEBUG)

# 使用 `backoff` 装饰器处理瞬时错误时的重试。
@backoff.on_exception(backoff.expo,
                      (requests.exceptions.Timeout,
                       requests.exceptions.ConnectionError),
                       max_time=30)
def get_auth_response():
    authorization_url = 'https://authorization.service/api/get_api_token'
    return requests.get(
        f'{authorization_url}',
        auth=(os.environ.get('AUTH_USER'), os.environ.get('AUTH_PWD'))
    )

# 在我们的示例中,访问令牌是从给定端点获取的
try:

    # 执行HTTP请求,响应示例:
    # { "Token" : "abcdefghijklmn" }
    response = get_auth_response()

    # 检查请求是否成功。可能引发 `requests.exceptions.HTTPError`
    response.raise_for_status()

    # 获取JSON数据
    response_body = response.json()

# 如果需要,可以捕获特定的异常

# requests.ConnectionError                  : 发生了网络连接错误问题

# requests.HTTPError                        : HTTP请求返回了不成功的状态码。[Response.raise_for_status()]

# requests.ConnectTimeout                   : 请求在尝试连接到远程服务器时超时

# requests.ReadTimeout                      : 服务器在指定时间内未发送任何数据。

# requests.TooManyRedirects                 : 请求超过了配置的最大重定向次数

# requests.exceptions.RequestException      : 与Requests相关的所有异常
except json.JSONDecodeError as json_decode_error:
    # 记录与解码JSON响应相关的错误
    logging.error(f'错误,解码JSON响应时失败。错误信息:{json_decode_error}')
    raise
except requests.exceptions.RequestException as requests_error:
    # 记录与`Requests`相关的异常
    logging.error(f'错误,执行HTTP请求时失败。错误信息:{requests_error}')
    raise
except Exception as e:
    # 记录其他任何错误
    logging.error(f'错误,检索访问令牌时发生未知错误。错误信息:{e}')
    raise

# 计算包含重写文件内容的对象。

# 它使用从请求中获取的数据
overrides_data = {
    "headers": {
        "Authorization": f"Token {response_body['Token']}"
    }
}

# 记录条目,告知文件重写计算情况
logging.info("正在创建重写文件:%s" % overrides_file_path)

# 尝试覆盖文件
try:
    if os.path.exists(overrides_file_path):
        os.unlink(overrides_file_path)

    # 用我们更新的字典覆盖文件
    with open(overrides_file_path, "wb+") as fd:
        fd.write(json.dumps(overrides_data).encode('utf-8'))
except Exception as e:
    # 记录其他任何错误
    logging.error(f'错误,覆盖文件 {overrides_file_path} 时发生未知错误。错误信息:{e}')
    raise

# 记录重写已成功完成的提示信息
logging.info("重写文件已更新")

结束


在 overrides 命令示例中,Python 脚本依赖于 `backoff` 库。为确保在执行 Python 脚本前安装该库,需将 `APISEC_PRE_SCRIPT` 设置为安装 overrides 命令依赖项的脚本。

例如,以下脚本 `user-pre-scan-set-up.sh`

```shell
#!/bin/bash

# user-pre-scan-set-up.sh

# 确保 Python 依赖已安装

echo "**** 安装 Python 依赖 ****"

sudo pip3 install --no-cache --upgrade --break-system-packages \
    backoff

echo "**** Python 依赖已安装 ****"

# 结束

您必须更新配置以将 APISEC_PRE_SCRIPT 设置为新脚本 user-pre-scan-set-up.sh。例如:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_PRE_SCRIPT: ./user-pre-scan-set-up.sh
  APISEC_OVERRIDES_FILE: dast-api-overrides.json
  APISEC_OVERRIDES_CMD: renew_token.py
  APISEC_OVERRIDES_INTERVAL: 300

在前面的示例中,您可以使用 user-pre-scan-set-up.sh 脚本来安装新的运行时或应用程序,之后可以在 overrides 命令中使用它们。

请求头

请求头功能允许您在扫描会话期间指定标头的固定值。例如,您可以使用配置变量 APISEC_REQUEST_HEADERSCache-Control 标头中设置固定值。如果需要设置的标头包含敏感值(如 Authorization 标头),请结合使用屏蔽变量功能和变量 APISEC_REQUEST_HEADERS_BASE64

Authorization 标头或其他标头需要在扫描进行中更新,请考虑使用overrides 功能。

变量 APISEC_REQUEST_HEADERS 允许您指定一个逗号分隔(,)的标头列表。这些标头会被包含在扫描器执行的每个请求中。列表中的每个标头条目由名称、冒号(:)及其值组成。名称或值前的空白会被忽略。例如,若要声明名为 Cache-Control 且值为 max-age=604800 的标头,条目应为 Cache-Control: max-age=604800。若要使用两个标头 Cache-Control: max-age=604800Age: 100,则将 APISEC_REQUEST_HEADERS 变量设置为 Cache-Control: max-age=604800, Age: 100

不同标头在 APISEC_REQUEST_HEADERS 变量中提供的顺序不会影响结果。将 APISEC_REQUEST_HEADERS 设为 Cache-Control: max-age=604800, Age: 100 与设为 Age: 100, Cache-Control: max-age=604800 效果相同。

Base64

APISEC_REQUEST_HEADERS_BASE64 变量接受的标头列表与 APISEC_REQUEST_HEADERS 相同,唯一区别是该变量的整个值必须是 Base64 编码的。例如,若要将 APISEC_REQUEST_HEADERS_BASE64 变量设置为 Authorization: QmVhcmVyIFRPS0VO, Cache-control: bm8tY2FjaGU=,请确保将列表转换为 Base64 等价形式:QXV0aG9yaXphdGlvbjogUW1WaGNtVnlJRlJQUzBWTywgQ2FjaGUtY29udHJvbDogYm04dFkyRmphR1U9,并使用 Base64 编码后的值。这在存储秘密标头值于屏蔽变量(存在字符集限制)时很有用。

Base64 用于支持屏蔽变量功能。Base64 编码本身并非安全措施,因为敏感值可轻易解码。

示例:使用纯文本在每个请求中添加标头列表

在以下 .gitlab-ci.yml 示例中,APISEC_REQUEST_HEADERS 配置变量被设置为提供两个标头值,如请求头所述。

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_REQUEST_HEADERS: 'Cache-control: no-cache, Save-Data: on'

示例:使用掩码CI/CD变量

以下 .gitlab-ci.yml 示例假设定义了掩码变量 SECRET_REQUEST_HEADERS_BASE64,该变量作为UI中定义的群组或实例CI/CD变量SECRET_REQUEST_HEADERS_BASE64 的值设置为 WC1BQ01FLVNlY3JldDogc31jcnt0ISwgWC1BQ01FLVRva2VuOiA3MDVkMTZmNWUzZmI=,这是 X-ACME-Secret: s3cr3t!, X-ACME-Token: 705d16f5e3fb 的Base64编码文本版本。然后可以按如下方式使用:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_REQUEST_HEADERS_BASE64: $SECRET_REQUEST_HEADERS_BASE64

当将秘密头值存储在掩码变量(具有字符集限制)中时,考虑使用 APISEC_REQUEST_HEADERS_BASE64

排除路径

测试API时,排除某些路径可能很有用。例如,你可能希望排除对身份验证服务或旧版API的测试。要排除路径,请使用 APISEC_EXCLUDE_PATHS CI/CD变量。此变量在你的 .gitlab-ci.yml 文件中指定。若要排除多个路径,请使用 ; 字符分隔条目。在提供的路径中,你可以使用单字符通配符 ? 和多字符通配符 *

要验证路径是否被排除,请查看作业输出的“已测试操作”和“已排除操作”部分。你不应在“已测试操作”下看到任何已排除的路径列表。

2021-05-27 21:51:08 [INF] API SECURITY: --[ 已测试操作 ]-------------------------
2021-05-27 21:51:08 [INF] API SECURITY: 201 POST http://target:7777/api/users CREATED
2021-05-27 21:51:08 [INF] API SECURITY: ------------------------------------------------
2021-05-27 21:51:08 [INF] API SECURITY: --[ 已排除操作 ]-----------------------
2021-05-27 21:51:08 [INF] API SECURITY: GET http://target:7777/api/messages
2021-05-27 21:51:08 [INF] API SECURITY: POST http://target:7777/api/messages
2021-05-27 21:51:08 [INF] API SECURITY: ------------------------------------------------

示例

此示例排除了 /auth 资源。这不排除子资源(/auth/child)。

variables:
  APISEC_EXCLUDE_PATHS: /auth

若要排除 /auth 及其子资源(/auth/child),我们使用通配符。

variables:
  APISEC_EXCLUDE_PATHS: /auth*

若要排除多个路径,我们使用 ; 字符。在此示例中,我们排除 /auth*/v1/*

variables:
  APISEC_EXCLUDE_PATHS: /auth*;/v1/*

若要排除路径内的一个或多个嵌套级别,我们使用 **。在此示例中,我们正在测试API端点。我们正在测试数据查询请求 massbrightnesscoordinates 数据的 /api/v1//api/v2/,对象包括 planetmoonstarsatellite。可能扫描的示例路径包括但不限于:

  • /api/v2/planet/coordinates
  • /api/v1/star/mass
  • /api/v2/satellite/brightness

在此示例中,我们仅测试 brightness 端点:

variables:
  APISEC_EXCLUDE_PATHS: /api/**/mass;/api/**/coordinates

排除参数

测试API时,你可能希望从测试中排除某个参数(查询字符串、标头或正文元素)。这可能是因为参数总是导致失败、减慢测试速度或其他原因。要排除参数,你可以设置以下变量之一:APISEC_EXCLUDE_PARAMETER_ENVAPISEC_EXCLUDE_PARAMETER_FILE

APISEC_EXCLUDE_PARAMETER_ENV 允许提供包含排除参数的JSON字符串。如果JSON较短且不常更改,这是一个不错的选择。另一个选项是变量 APISEC_EXCLUDE_PARAMETER_FILE。此变量设置为文件路径,该路径可以检入仓库、由其他作业作为工件创建,或在运行时通过预脚本使用 APISEC_PRE_SCRIPT 生成。

使用JSON文档排除参数

该JSON文档包含一个JSON对象,此对象使用特定的属性来标识应排除哪些参数。 您可以在扫描过程中提供以下属性以排除特定参数:

  • headers:使用此属性排除特定头部。该属性的值是要排除的头部名称数组。名称不区分大小写。
  • cookies:使用此属性的值排除特定Cookie。该属性的值是要排除的Cookie名称数组。名称区分大小写。
  • query:使用此属性从查询字符串中排除特定字段。该属性的值是从查询字符串中要排除的字段名称数组。名称区分大小写。
  • body-form:使用此属性从使用媒体类型 application/x-www-form-urlencoded 的请求中排除特定字段。该属性的值是从正文中要排除的字段名称数组。名称区分大小写。
  • body-json:使用此属性从使用媒体类型 application/json 的请求中排除特定JSON节点。该属性的值是一个数组,数组的每个条目都是一个 JSON Path 表达式。
  • body-xml:使用此属性从使用媒体类型 application/xml 的请求中排除特定XML节点。该属性的值是一个数组,数组的每个条目都是一个 XPath v2 表达式。

因此,以下JSON文档是预期结构的示例,用于排除参数。

{
  "headers": [
    "header1",
    "header2"
  ],
  "cookies": [
    "cookie1",
    "cookie2"
  ],
  "query": [
    "query-string1",
    "query-string2"
  ],
  "body-form": [
    "form-param1",
    "form-param2"
  ],
  "body-json": [
    "json-path-expression-1",
    "json-path-expression-2"
  ],
  "body-xml" : [
    "xpath-expression-1",
    "xpath-expression-2"
  ]
}

示例

排除单个头部

若要排除头部 Upgrade-Insecure-Requests,请将 header 属性的值设置为一个包含头部名称的数组:[ "Upgrade-Insecure-Requests" ]。例如,JSON文档如下所示:

{
  "headers": [ "Upgrade-Insecure-Requests" ]
}

头部名称不区分大小写,因此头部名称 UPGRADE-INSECURE-REQUESTSUpgrade-Insecure-Requests 等效。

同时排除一个头部和两个Cookie

若要排除头部 Authorization 以及Cookie PHPSESSIDcsrftoken,请将 headers 属性的值设置为包含头部名称的数组 [ "Authorization" ],并将 cookies 属性的值设置为包含Cookie名称的数组 [ "PHPSESSID", "csrftoken" ]。例如,JSON文档如下所示:

{
  "headers": [ "Authorization" ],
  "cookies": [ "PHPSESSID", "csrftoken" ]
}
排除 body-form 参数

若要排除使用 application/x-www-form-urlencoded 的请求中的 password 字段,请将 body-form 属性的值设置为包含字段名称的数组 [ "password" ]。例如,JSON文档如下所示:

{
  "body-form":  [ "password" ]
}

当请求使用内容类型 application/x-www-form-urlencoded 时,排除参数会使用 body-form

使用JSON Path排除特定JSON节点

若要排除根对象中的 schema 属性,请将 body-json 属性的值设置为一个包含JSON Path表达式的数组 [ "$.schema" ]

JSON Path表达式使用特殊语法来标识JSON节点:$ 指向JSON文档的根,. 指向当前对象(在我们的案例中是根对象),而文本 schema 指向属性名。因此,JSON路径表达式 $.schema 指向根对象中的一个名为 schema 的属性。 例如,JSON文档如下所示:

{
  "body-json": [ "$.schema" ]
}

当请求使用内容类型 application/json 时,排除参数会使用 body-jsonbody-json 中的每个条目都应是一个 JSON Path 表达式。在JSON Path中,字符如 $*. 等具有特殊含义。

使用JSON路径排除多个JSON节点

若要排除根级 users 数组中每个条目的 password 属性,需将 body-json 属性的值设为一个包含JSON路径表达式的数组 [ "$.users[*].password" ]

JSON路径表达式以 $ 开头表示根节点,. 表示当前节点;接着通过 users 引用属性,用 [] 包裹要使用的数组索引——此处用 * 替代具体数字来指定任意索引;索引引用后紧跟 .,此时该符号指向数组中被选中的任意索引,其前是属性名 password

例如,对应的JSON文档如下:

{
  "body-json": [ "$.users[*].password" ]
}

当请求的内容类型为 application/json 时,排除参数会使用 body-jsonbody-json 中的每一项都应是一个 JSON Path表达式。在JSON Path中,$*. 等字符具有特殊含义。

排除XML属性

若要排除根元素 credentials 中名为 isEnabled 的属性,需将 body-xml 属性的值设为一个包含XPath表达式的数组 [ "/credentials/@isEnabled" ]

XPath表达式 /credentials/@isEnabled/ 开头表示XML文档的根,其后跟随 credentials 指定要匹配的元素名;再用 / 引用前一XML元素的节点,@ 则表明 isEnabled 是属性。

例如,对应的JSON文档如下:

{
  "body-xml": [
    "/credentials/@isEnabled"
  ]
}

当请求的内容类型为 application/xml 时,排除参数会使用 body-xmlbody-xml 中的每一项都应是一个 XPath v2表达式。在XPath表达式中,@/:[] 等字符具有特殊含义。

排除XML文本元素

若要排除根节点 credentialsusername 元素的文本内容,需将 body-xml 属性的值设为一个包含XPath表达式的数组 [ "/credentials/username/text()" ]

在XPath表达式 /credentials/username/text() 中,首字符 / 指向XML根节点,其后 credentials 表示XML元素名;接着的 / 引用当前元素,后面跟着新的XML元素名 username;最后的 / 引用当前元素,并使用 text() 函数获取当前元素的文本。

例如,对应的JSON文档如下:

{
  "body-xml": [
    "/credentials/username/text()"
  ]
}

当请求的内容类型为 application/xml 时,排除参数会使用 body-xmlbody-xml 中的每一项都应是一个 XPath v2表达式。在XPath表达式中,@/:[] 等字符具有特殊含义。

排除XML元素

若要排除根节点 credentials 中的 username 元素,需将 body-xml 属性的值设为一个包含XPath表达式的数组 [ "/credentials/username" ]

在XPath表达式 /credentials/username 中,首字符 / 指向XML根节点,其后 credentials 表示XML元素名;接着的 / 引用当前元素,后面跟着新的XML元素名 username

例如,对应的JSON文档如下:

{
  "body-xml": [
    "/credentials/username"
  ]
}

当请求的内容类型为 application/xml 时,排除参数会使用 body-xmlbody-xml 中的每一项都应是一个 XPath v2表达式。在XPath表达式中,@/:[] 等字符具有特殊含义。

排除带有命名空间的XML节点

若要排除定义在命名空间s下的XML元素login(该元素位于credentials根节点内),需将body-xml属性值设为数组,其中包含XPath表达式[ "/credentials/s:login" ]

在XPath表达式/credentials/s:login中,首字符/代表XML根节点,其后紧跟的是XML元素名credentials。同理,字符/表示当前元素,后跟新的XML元素名s:login。注意名称中包含字符:,该字符用于分隔命名空间与节点名。

命名空间名称应在请求体所属的XML文档中预先定义。你可在规范文档HAR、OpenAPI或Postman集合文件中查看命名空间信息。

{
  "body-xml": [
    "/credentials/s:login"
  ]
}

当请求使用application/xml内容类型时,排除参数会使用body-xmlbody-xml中的每一项都应为XPath v2表达式。在XPath中,@/:[]等字符具有特殊含义。

使用JSON字符串

若要通过JSON文档设置排除规则,可将环境变量APISEC_EXCLUDE_PARAMETER_ENV设为JSON字符串。以下示例展示了如何在.gitlab-ci.yml中设置该变量:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_EXCLUDE_PARAMETER_ENV: '{ "headers": [ "Upgrade-Insecure-Requests" ] }'

使用文件

若要通过JSON文档设置排除规则,可将环境变量APISEC_EXCLUDE_PARAMETER_FILE设为JSON文件路径。该路径相对于作业当前工作目录。以下示例展示了如何在.gitlab-ci.yml中设置该变量:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_PROFILE: Quick
  APISEC_OPENAPI: test-api-specification.json
  APISEC_TARGET_URL: http://test-deployment/
  APISEC_EXCLUDE_PARAMETER_FILE: dast-api-exclude-parameters.json

dast-api-exclude-parameters.json是一个遵循排除参数文档结构的JSON文档。

排除URL

作为按路径排除的替代方案,你可通过对URL的其他组件进行过滤来排除请求,方法是使用CI/CD变量APISEC_EXCLUDE_URLS。该变量可在.gitlab-ci.yml文件中设置,支持以逗号分隔的多个值,每个值均为正则表达式。由于每项都是正则表达式,因此类似.*的条目会排除所有URL(因其匹配所有内容)。

你可在作业输出中检查哪些URL匹配了APISEC_EXCLUDE_URLS提供的正则表达式。匹配操作会在Excluded Operations部分列出。这些操作不应出现在Tested Operations部分中。例如以下作业输出的片段:

2021-05-27 21:51:08 [INF] API SECURITY: --[ Tested Operations ]-------------------------
2021-05-27 21:51:08 [INF] API SECURITY: 201 POST http://target:7777/api/users CREATED
2021-05-27 21:51:08 [INF] API SECURITY: ------------------------------------------------
2021-05-27 21:51:08 [INF] API SECURITY: --[ Excluded Operations ]-----------------------
2021-05-27 21:51:08 [INF] API SECURITY: GET http://target:7777/api/messages
2021-05-27 21:51:08 [INF] API SECURITY: POST http://target:7777/api/messages
2021-05-27 21:51:08 [INF] API SECURITY: ------------------------------------------------

APISEC_EXCLUDE_URLS中的每个值都是正则表达式。正则表达式.*$等许多字符具有特殊含义。

示例

排除URL及其子资源

以下示例排除了URL http://target/api/auth及其子资源。

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_TARGET_URL: http://target/
  APISEC_OPENAPI: test-api-specification.json
  APISEC_EXCLUDE_URLS: http://target/api/auth
排除两个URL并允许其子资源

若要排除URL http://target/api/buyhttp://target/api/sell 但允许扫描它们的子资源(例如:http://target/api/buy/toyhttp://target/api/sell/chair),你可以使用值 http://target/api/buy/$,http://target/api/sell/$。该值使用了两个正则表达式,每个正则表达式由 , 字符分隔。因此它包含 http://target/api/buy$http://target/api/sell$。在每个正则表达式中,末尾的 $ 字符指明匹配的URL应在此处结束。

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_TARGET_URL: http://target/
  APISEC_OPENAPI: test-api-specification.json
  APISEC_EXCLUDE_URLS: http://target/api/buy/$,http://target/api/sell/$
排除两个URL及其子资源

若要排除URL:http://target/api/buyhttp://target/api/sell 及其子资源。提供多个URL时,我们按如下方式使用 , 字符:

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_TARGET_URL: http://target/
  APISEC_OPENAPI: test-api-specification.json
  APISEC_EXCLUDE_URLS: http://target/api/buy,http://target/api/sell
使用正则表达式排除URL

若要精确排除 https://target/api/v1/user/createhttps://target/api/v2/user/create 或其他任何版本(v3v4 等),我们可以使用 https://target/api/v.*/user/create$。在前面的正则表达式中,. 表示任意字符,* 表示零次或多次,此外 $ 表示URL应在此处结束。

stages:
  - dast

include:
  - template: API-Security.gitlab-ci.yml

variables:
  APISEC_TARGET_URL: http://target/
  APISEC_OPENAPI: test-api-specification.json
  APISEC_EXCLUDE_URLS: https://target/api/v.*/user/create$