自定义分析器设置

可以通过 CI/CD 变量来修改 API 模糊测试的行为。

API 模糊测试的配置文件必须在仓库的 .gitlab 目录下。

caution所有极狐GitLab 安全扫描工具的自定义配置都应该在合并请求变更被合并到默认分支前进行测试。失败的话会带来很多意料之外的结果,包括大量的假阳性问题。

认证

认证是通过将身份验证令牌作为标头或 cookie 来处理的。您可以提供执行身份验证流或计算令牌的脚本。

HTTP Basic 认证

HTTP basic 认证是一种内置于 HTTP 写一种的身份验证方法,通常和传输层安全(TLS)一起使用。

我们推荐您为密码创建了一个 CI/CD 变量(比如,TEST_API_PASSWORD),并将其设置为被屏蔽。您可以通过访问项目的极狐GitLab 页面上的 设置 > CI/CD,在 变量 部分创建 CI/CD 变量。由于屏蔽变量的限制,您应该在添加变量之前对密码进行 Base64 编码。

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

  • FUZZAPI_HTTP_USERNAME:认证的用户名。
  • FUZZAPI_HTTP_PASSWORD_BASE64:Base64 编码的密码。
stages:
    - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick-10
  FUZZAPI_HAR: test-api-recording.har
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_HTTP_USERNAME: testuser
  FUZZAPI_HTTP_PASSWORD_BASE64: $TEST_API_PASSWORD

原始密码

如果您不想要对密码进行 Base64 编码(或者您正在使用极狐GitLab 15.3 或更早版本),您可以使用 FUZZAPI_HTTP_PASSWORD 提供原始密码,而不是使用 FUZZAPI_HTTP_PASSWORD_BASE64

Bearer 令牌

Bearer 令牌由被不同的认证机制所使用,报错 OAuth2 或 JSON Web Tokens(JWT)。Bearer 令牌通过 Authorization HTTP 标头传输。要在 API 模糊测试中使用 bearer 令牌,您需要以下之一:

  • 一个未过期的令牌。
  • 生成一个在测试期间持续存在的令牌的方法。
  • API 模糊测试可以调用的 Python 脚本来生成令牌。

令牌不过期

如果 bearer 令牌不过期,您可以使用 FUZZAPI_OVERRIDES_ENV 变量来提供它。此变量的内容是一个 JSON 片段,提供要添加到 API 模糊测试的传出 HTTP 请求中的标头和 cookie。

遵循这些步骤来提供具有 FUZZAPI_OVERRIDES_ENV 的 bearer 令牌:

  1. 创建 CI/CD 变量,比如 TEST_API_BEARERAUTH,值为 {"headers":{"Authorization":"Bearer dXNlcm5hbWU6cGFzc3dvcmQ="}} (替换您的令牌)。您可以在极狐GitLab 项目的页面上创建 CI/CD 变量,设置 > CI/CD,在变量部分。

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

    stages:
      - fuzz
    
    include:
      - template: API-Fuzzing.gitlab-ci.yml
    
    variables:
      FUZZAPI_PROFILE: Quick-10
      FUZZAPI_OPENAPI: test-api-specification.json
      FUZZAPI_TARGET_URL: http://test-deployment/
      FUZZAPI_OVERRIDES_ENV: $TEST_API_BEARERAUTH
    
  3. 为了验证认证是正常工作的,运行一个 API 模糊测试并查看模糊测试日志和测试 API 的应用程序日志。有关重写命令的更多信息,请参阅重写部分

在测试运行时生成令牌

如果必须要生成 bearer 令牌而且不能在测试期间过去,您可以为 API 模糊测试提供一个包含令牌的文件。之前的一个 stage 和作业,或作为 API 模糊作业的一部分,可以生成此文件。

API 模糊测试期望接收具有以下结构的 JSON 文件:

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

此文件可以由一个先前的 stage 生成,并通过 FUZZAPI_OVERRIDES_FILE CI/CD 变量提供给 API 模糊测试。

在您的 .gitlab-ci.yml 文件中设置 FUZZAPI_OVERRIDES_FILE

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_OVERRIDES_FILE: api-fuzzing-overrides.json

要验证认证能够给正常工作,运行 API 模糊测试并查看模糊测试日志和测试 API 的应用程序日志。

令牌有短的过期时间

如果必须要生成 bearer 令牌并在扫描完成之前就会过期,那么您可以为 API 模糊测试提供一个程序或脚本,以便在提供的间隔上执行。提供的脚本在 Alpine Linux 容器中运行,该容器安装了 Python 3 和 Bash。如果 Python 脚本需要额外的包,它必须检测到这一点并在运行时安装这些包。

脚本必须创建一个包含了如下格式的 bearer 令牌的 JSON 文件:

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

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

  • FUZZAPI_OVERRIDES_FILE: 生成的 JSON 文件。
  • FUZZAPI_OVERRIDES_CMD: 生成 JSON 文件的命令。
  • FUZZAPI_OVERRIDES_INTERVAL: 运行命令的间隔(以秒为单位)。

比如:

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick-10
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_OVERRIDES_FILE: api-fuzzing-overrides.json
  FUZZAPI_OVERRIDES_CMD: renew_token.py
  FUZZAPI_OVERRIDES_INTERVAL: 300

要验证认证能够给正常工作,运行 API 模糊测试并查看模糊测试日志和测试 API 的应用程序日志。

API 模糊测试资料

极狐GitLab 提供的配置文件 gitlab-api-fuzzing-config.yml 包含几个测试配置文件,执行特定数量的测试。随着测试数量的增加,每个配置文件的运行时间也会增加。

资料 模糊测试(每个参数)
Quick-10 10
Medium-20 20
Medium-50 50
Long-100 100

覆盖

API 模糊测试提供方法在您的请求中添加或覆盖特定的项目,例如:

  • Headers
  • Cookies
  • Query string
  • Form data
  • JSON nodes
  • XML nodes

您可以使用这些项目来注入语义版本标头、身份验证等。身份验证部分包括使用覆盖进行此目的的示例。

覆盖使用 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"
  }
}

设置单个标头的示例:

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

同时设置标头和 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 对象中的每一个属性名称都设置为一个 XPath 表达式。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 文档,设置 FUZZAPI_OVERRIDES_FILE CI/CD 变量。路径是相对于作业当前工作目录的。

以下是 .gitlab-ci.yml 示例:

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_OVERRIDES_FILE: api-fuzzing-overrides.json

使用 CI/CD 变量

要以 CI/CD 变量的形式提供覆盖的 JSON,使用 FUZZAPI_OVERRIDES_ENV 变量。这允许您将 JSON 作为可隐藏和保护的变量。

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

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_OVERRIDES_ENV: '{"headers":{"X-API-Version":"2"}}'

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

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_OVERRIDES_ENV: $SECRET_OVERRIDES

使用命令

如果值必须生成或在到期时重新生成,您可以提供一个程序或脚本供 API 模糊测试在指定间隔执行。提供的脚本在安装了 Python 3 和 Bash 的 Alpine Linux 容器中运行。

您必须将环境变量 FUZZAPI_OVERRIDES_CMD 设置为要执行的程序或脚本。提供的脚本创建先前定义的覆盖 JSON 文件。

您可能还想要安装其他脚本运行时,如 NodeJS 或 Ruby,或者您可能需要为覆盖命令安装依赖项。在这种情况下,您应该将 FUZZAPI_PRE_SCRIPT 设置为提供这些先决条件的脚本的文件路径。在分析器开始之前,提供的脚本执行一次。

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

您必须提供三个环境变量,每个变量都设置为正确的操作:

  • FUZZAPI_OVERRIDES_FILE: 由提供的命令生成的覆盖 JSON 文件。
  • FUZZAPI_OVERRIDES_CMD: 负责定期生成覆盖 JSON 文件的覆盖命令。
  • FUZZAPI_OVERRIDES_INTERVAL: 以秒为单位运行命令的间隔。

可选地:

  • FUZZAPI_PRE_SCRIPT:在 runner 开始之前安装运行时或依赖项的脚本。
caution要在 Alpine Linux 上执行脚本,您必须使用 chmod 来设置执行权限。比如,要为所有人设置 script.py 的执行权限,使用命令:sudo chmod a+x script.py。如果需要,您可以在 script.py 中设置执行权限。
stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_OVERRIDES_FILE: api-fuzzing-overrides.json
  FUZZAPI_OVERRIDES_CMD: renew_token.py
  FUZZAPI_OVERRIDES_INTERVAL: 300

调试覆盖

默认情况下,覆盖命令的输出是被隐藏的。如果覆盖命令返回了非零退出代码,该命令将作为作业输出的一部分显示。可选地,您可以通过将变量 FUZZAPI_OVERRIDES_CMD_VERBOSE 设置为任何值来显示覆盖命令输出,就像它被生成时一样。这在测试覆盖脚本时很有用,但之后应该禁用它,因为它会减慢测试速度。

还能够将脚本中的消息写入日志文件,该文件在作业完成或失败时收集。日志文件必须保存在特定位置,并遵循特定的命名约定。

在您的覆盖脚本中添加一些基本的日志是很有用的,以防在作业的典型运行期间脚本意外失败。日志文件作为作业的工件自动收集,允许您在作业完成后下载它。

在我们的示例中,我们在环境变量 FUZZAPI_OVERRIDES_CMD 中提供了 renew_token.py。注意脚本中的两件事:

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

# Example of an overrides command

# Override commands can update the overrides json file
# with new values to be used.  This is a great way to
# update an authentication token that will expire
# during testing.

import logging
import json
import os
import requests
import backoff

# [1] Store log file in directory indicated by env var CI_PROJECT_DIR
working_directory = os.environ.get( 'CI_PROJECT_DIR')
overrides_file_name = os.environ.get('FUZZAPI_OVERRIDES_FILE', 'api-fuzzing-overrides.json')
overrides_file_path = os.path.join(working_directory, overrides_file_name)

# [2] File name should match the pattern: gl-*.log
log_file_path = os.path.join(working_directory, 'gl-user-overrides.log')

# Set up logger
logging.basicConfig(filename=log_file_path, level=logging.DEBUG)

# Use `backoff` decorator to retry in case of transient errors.
@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'))
    )

# In our example, access token is retrieved from a given endpoint
try:

    # Performs a http request, response sample:
    # { "Token" : "abcdefghijklmn" }
    response = get_auth_response()

    # Check that the request is successful. may raise `requests.exceptions.HTTPError`
    response.raise_for_status()

    # Gets JSON data
    response_body = response.json()

# If needed specific exceptions can be caught
# requests.ConnectionError                  : A network connection error problem occurred
# requests.HTTPError                        : HTTP request returned an unsuccessful status code. [Response.raise_for_status()]
# requests.ConnectTimeout                   : The request timed out while trying to connect to the remote server
# requests.ReadTimeout                      : The server did not send any data in the allotted amount of time.
# requests.TooManyRedirects                 : The request exceeds the configured number of maximum redirections
# requests.exceptions.RequestException      : All exceptions that related to Requests
except json.JSONDecodeError as json_decode_error:
    # logs errors related decoding JSON response
    logging.error(f'Error, failed while decoding JSON response. Error message: {json_decode_error}')
    raise
except requests.exceptions.RequestException as requests_error:
    # logs  exceptions  related to `Requests`
    logging.error(f'Error, failed while performing HTTP request. Error message: {requests_error}')
    raise
except Exception as e:
    # logs any other error
    logging.error(f'Error, unknown error while retrieving access token. Error message: {e}')
    raise

# computes object that holds overrides file content.
# It uses data fetched from request
overrides_data = {
    "headers": {
        "Authorization": f"Token {response_body['Token']}"
    }
}

# log entry informing about the file override computation
logging.info("Creating overrides file: %s" % overrides_file_path)

# attempts to overwrite the file
try:
    if os.path.exists(overrides_file_path):
        os.unlink(overrides_file_path)

    # overwrites the file with our updated dictionary
    with open(overrides_file_path, "wb+") as fd:
        fd.write(json.dumps(overrides_data).encode('utf-8'))
except Exception as e:
    # logs any other error
    logging.error(f'Error, unknown error when overwriting file {overrides_file_path}. Error message: {e}')
    raise

# logs informing override has finished successfully
logging.info("Override file has been updated")

# end

在覆盖命令行示例中,Python 脚本依赖于 backoff 库。为了确保在执行 Python 脚本之前安装了库,FUZZAPI_PRE_SCRIPT 被设置为安装覆盖命令的依赖项的脚本。

比如,如下的 user-pre-scan-set-up.sh 脚本:

#!/bin/bash

# user-pre-scan-set-up.sh
# Ensures python dependencies are installed

echo "**** install python dependencies ****"

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

echo "**** python dependencies installed ****"

# end

您不得不更新您的配置来为我们新的 user-pre-scan-set-up.sh 脚本设置 FUZZAPI_PRE_SCRIPT。例如:

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_PRE_SCRIPT: user-pre-scan-set-up.sh
  FUZZAPI_OVERRIDES_FILE: api-fuzzing-overrides.json
  FUZZAPI_OVERRIDES_CMD: renew_token.py
  FUZZAPI_OVERRIDES_INTERVAL: 300

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

排除路径

当测试 API 时,它在排除特定路径时是非常有帮助的。比如,您可以能会排除测试身份验证服务或旧版本的 API。要排除路径,请使用 FUZZAPI_EXCLUDE_PATHS CI/CD 变量。此变量在您的 .gitlab-ci.yml 文件中指定。要排除多个路径,请使用 ; 字符分隔条目。在提供的路径中,您可以使用单个字符通配符 ?* 用于多个字符通配符。

要验证排除的路径,请查看作业输出中的 Tested OperationsExcluded Operations 部分。您不应该在 Tested Operations 下看到任何排除的路径。

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

排除路径的示例

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

variables:
  FUZZAPI_EXCLUDE_PATHS: /auth

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

variables:
  FUZZAPI_EXCLUDE_PATHS: /auth*

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

variables:
  FUZZAPI_EXCLUDE_PATHS: /auth*;/v1/*

排除参数

当测试 API 时,您可能想要从测试中排除某些参数(查询字符串、标头或请求体元素)。这种需要可能是因为总是有参数会引起失败,从而减慢测试,或出于其他原因。要排除参数,您可以使用以下变量之一:FUZZAPI_EXCLUDE_PARAMETER_ENVFUZZAPI_EXCLUDE_PARAMETER_FILE

FUZZAPI_EXCLUDE_PARAMETER_ENV 允许提供包含参数的 JSON 字符串。如果 JSON 短且不太可能经常更改,这可能是个好选择。另一种选择是变量 FUZZAPI_EXCLUDE_PARAMETER_FILE。此变量设置为可以签入存储库、由另一个作业作为工件创建或使用 FUZZAPI_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 是一样的。

同时排除标头和两个 cookies

要排除标头 Authorization 和 cookies PHPSESSIDcsrftoken,将 headers 属性的值设置为包含标头名称的数组 [ "Authorization" ],将 cookies 属性的值设置为包含 cookies 名称的数组 [ "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 路径表达式使用特殊的语法来标识 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 Path 表达式的数组 [ "$.users[*].paswword" ]

JSON Path 表达式以 $ 开头表示根节点,然后使用 . 表示当前节点。然后,它使用 users 表示属性,并使用 [] 包含要使用的数组中的索引,而不是提供数字作为索引,我们使用 * 来指定任何索引。在索引引用之后,我们找到 .,现在它引用的是任何给定选择的索引,前面是属性名称 password

比如,JSON 文档如下所示:

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

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

排除 XML 属性

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

XPath 表达式 /credentials/@isEnabled,以 / 开头,表示 XML 文档的根,然后是单词 credentials,表示要匹配的元素的名称。它使用 / 来引用上一个 XML 元素的节点,并使用字符 @ 来表示名称 isEnable 是一个属性。

比如,JSON 文档如下所示:

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

当请求使用上下文类型 application/xml 时,排除参数使用 body-xmlbody-xml 中的每个条目都应是 XPath v2 表达式。在 XPath 表达式中,字符如 @/:[] 等具有特殊含义。

排除 XML 元素的文本

要排除在根节点 credentials 中的 username 元素的文本,将 body-xml 属性的值设置为包含 XPath 表达式的数组 [ "/credentials/username/text()" ]

在 XPath 表达式 /credentials/username/text() 中,第一个字符 / 指示 XML 文档的根节点,然后它后面指示 XML 元素的名称 credentials。同样,字符 / 指示当前元素,后跟新 XML 元素的名称 username。最后一个部分有一个 /,它引用当前元素,并使用 XPath 函数 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 文档的根节点,然后它后面指示 XML 元素的名称 credentials。同样,字符 / 指示当前元素,后跟新 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 Collection 文件的规范文档中检查命名空间。

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

当请求使用上下文类型 application/xml 时,排除参数使用 body-xmlbody-xml 中的每个条目都应是 XPath v2 表达式。在 XPath 表达式中,字符如 @/:[] 等具有特殊含义。

使用 JSON 字符串

要想提供排除的 JSON 文档,要用 JSON 字符串设置变量 FUZZAPI_EXCLUDE_PARAMETER_ENV。在以下示例 .gitlab-ci.yml 文件中,FUZZAPI_EXCLUDE_PARAMETER_ENV 变量设置为 JSON 字符串:

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_EXCLUDE_PARAMETER_ENV: '{ "headers": [ "Upgrade-Insecure-Requests" ] }'

使用文件

要提供排除的 JSON 文档,在 JSON 文件路径中设置变量 FUZZAPI_EXCLUDE_PARAMETER_FILE。文件路径相对于作业当前工作目录。在以下示例 .gitlab-ci.yml 文件中,FUZZAPI_EXCLUDE_PARAMETER_FILE 变量设置为 JSON 文件路径:

stages:
     - fuzz

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

variables:
  FUZZAPI_PROFILE: Quick
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_TARGET_URL: http://test-deployment/
  FUZZAPI_EXCLUDE_PARAMETER_FILE: api-fuzzing-exclude-parameters.json

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

排除 URL

作为通过路径排除的替代方法,您可以使用 FUZZAPI_EXCLUDE_URLS CI/CD 变量按任何其他 URL 组件进行过滤。可以在 .gitlab-ci.yml 文件中设置此变量。该变量可以存储多个值,用逗号(,)分隔。每个值都是一个正则表达式。因为每个条目都是正则表达式,所以像 .* 这样的条目会排除所有 URL,因为它是一个匹配所有内容的正则表达式。

在您的作业输出中,您可以检查任何提供的正则表达式是否与 FUZZAPI_EXCLUDE_URLS 中的任何 URL 匹配。匹配操作在 排除的操作 部分中列出。在 排除的操作 中列出的操作不应在 测试操作 部分中列出。例如,作业输出的以下部分:

2021-05-27 21:51:08 [INF] API Fuzzing: --[ Tested Operations ]-------------------------
2021-05-27 21:51:08 [INF] API Fuzzing: 201 POST http://target:7777/api/users CREATED
2021-05-27 21:51:08 [INF] API Fuzzing: ------------------------------------------------
2021-05-27 21:51:08 [INF] API Fuzzing: --[ Excluded Operations ]-----------------------
2021-05-27 21:51:08 [INF] API Fuzzing: GET http://target:7777/api/messages
2021-05-27 21:51:08 [INF] API Fuzzing: POST http://target:7777/api/messages
2021-05-27 21:51:08 [INF] API Fuzzing: ------------------------------------------------
noteFUZZAPI_EXCLUDE_URLS 中的每个值都是正则表达式。诸如 .*$ 等一类的字符在正则表达式中有特殊含义。

示例

排除单个 URL 和其子资源

如下示例排除了 http://target/api/auth URL 和其子资源:

stages:
  - fuzz

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

variables:
  FUZZAPI_TARGET_URL: http://target/
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_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:
  - fuzz

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

variables:
  FUZZAPI_TARGET_URL: http://target/
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_EXCLUDE_URLS: http://target/api/buy/$,http://target/api/sell/$

排除两个 URL 及其子资源

要排除 URL http://target/api/buyhttp://target/api/sell,并排除其子资源。要提供多个 URL,我们使用 . 字符,如下所示:

stages:
  - fuzz

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

variables:
  FUZZAPI_TARGET_URL: http://target/
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_EXCLUDE_URLS: http://target/api/buy,http://target/api/sell

使用正则表达式排除 URL

要精确排除 https://target/api/v1/user/createhttps://target/api/v2/user/create 或任何其他版本 (v3,v4 等),我们可以使用 https://target/api/v.*/user/create$。在前面的正则表达式中:

  • . 表示任何字符。
  • * 表示零次或多次。
  • $ 表示 URL 应该在那里结束。
stages:
  - fuzz

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

variables:
  FUZZAPI_TARGET_URL: http://target/
  FUZZAPI_OPENAPI: test-api-specification.json
  FUZZAPI_EXCLUDE_URLS: https://target/api/v.*/user/create$

标头模糊

默认情况下标头模糊是被禁用的,因为许多技术堆栈中出现的大量假阳性。当启用标头模糊时,您必须指定要包含在模糊中的标头列表。

默认配置文件中的每个配置文件都有一个 GeneralFuzzingCheck 条目。此检查执行标头模糊。在 Configuration 部分下,您必须更改 HeaderFuzzingHeaders 设置以启用标头模糊。

此代码片段显示了禁用标头模糊的 Quick-10 配置文件的默认配置:

- Name: Quick-10
  DefaultProfile: Empty
  Routes:
  - Route: *Route0
    Checks:
    - Name: FormBodyFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true
    - Name: GeneralFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true
        HeaderFuzzing: false
        Headers:
    - Name: JsonFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true
    - Name: XmlFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true

HeaderFuzzing 是一个布尔值,用于打开和关闭标头模糊。默认设置为 false 关闭。要打开标头模糊,请将此设置更改为 true

    - Name: GeneralFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true
        HeaderFuzzing: true
        Headers:

Headers 是要模糊测试的标头列表。只有在列表中的标头才会被模糊测试。要模糊测试您 API 使用的自定义标头,请添加一个条目,使用语法 - Name: HeaderName。例如,要模糊测试自定义标头 X-Custom,请添加 - Name: X-Custom

    - Name: GeneralFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true
        HeaderFuzzing: true
        Headers:
          - Name: X-Custom

现在您有一个配置来模糊测试标头 X-Custom。使用相同的符号列出其他标头:

    - Name: GeneralFuzzingCheck
      Configuration:
        FuzzingCount: 10
        UnicodeFuzzing: true
        HeaderFuzzing: true
        Headers:
          - Name: X-Custom
          - Name: X-AnotherHeader

对于每一个需要的配置文件,重复此配置即可。