泄露密钥时的自动响应

引入于极狐GitLab 13.6。

极狐GitLab密钥检测在发现某些类型的泄露机密时会自动响应,可以:

  • 自动撤销密钥。
  • 通知发布密钥的合作伙伴。然后合作伙伴可以撤销密钥,通知其所有者,或以其他方式防止滥用。

支持的密钥类型和操作

极狐GitLab 自动响应支持以下类型的密钥:

密钥类型 执行的操作 SaaS 私有化部署
极狐GitLab 个人访问令牌 立即撤销令牌,发送电子邮件给所有者 ✅ (15.9 及更高版本)
Amazon Web Services (AWS) IAM 访问密钥 通知 AWS

说明

  • ✅ - 默认可用
  • ⚙ - 需要使用令牌撤销 API 进行手动集成

可用的功能

  • 为非默认分支启用于 15.11 版本。

凭据仅在密钥检测发现它们时才进行后处理:

  • 支持公开项目,因为公开暴露的凭据构成了更大的威胁。
  • 支持旗舰版项目。

高层架构

下图描述了后处理钩子如何撤销极狐GitLab 应用程序中的密钥:

sequenceDiagram autonumber GitLab Rails-->+GitLab Rails: gl-secret-detection-report.json GitLab Rails->>+GitLab Sidekiq: StoreScansService GitLab Sidekiq-->+GitLab Sidekiq: ScanSecurityReportSecretsWorker GitLab Sidekiq-->+GitLab Token Revocation API: GET revocable keys types GitLab Token Revocation API-->>-GitLab Sidekiq: OK GitLab Sidekiq->>+GitLab Token Revocation API: POST revoke revocable keys GitLab Token Revocation API-->>-GitLab Sidekiq: ACCEPTED GitLab Token Revocation API-->>+Partner API: revoke revocable keys Partner API-->>+GitLab Token Revocation API: ACCEPTED
  1. 带有密钥检测作业的流水线完成,生成扫描报告 (1)。
  2. 该报告由一个服务类处理 (2),如果可以撤销令牌,该服务类会安排一个异步 worker。
  3. 异步 worker (3) 与外部部署的 HTTP 服务(45)通信,来确定可以自动撤销哪些类型的密钥。
  4. Worker 发送(67)极狐GitLab 令牌撤销 API 能够撤销的检测到的密钥列表。
  5. 极狐GitLab 令牌撤销 API 将(89)每个可撤销令牌发送到各自供应商的 Partner API

泄露凭据通知的合作伙伴计划

当 SaaS 上的公开仓库中泄露他们颁发的凭据时,极狐GitLab 会通知合作伙伴。合作伙伴必须实施合作伙伴 API,其由极狐GitLab 令牌撤销 API 调用。

实施合作伙伴 API

合作伙伴 API 与极狐GitLab 令牌撤销 API 集成,来接收和响应泄漏的令牌撤销请求。该服务应该是一个可公开访问的 HTTP API,具有幂等性和速率限制。

对您的服务的请求可以包含一个或多个泄露的令牌,以及带有请求正文签名的 header,建议您使用此签名验证传入请求,以证明它是来自极狐GitLab 的真实请求。下图详细说明了接收、验证和撤销泄漏令牌的必要步骤:

sequenceDiagram autonumber GitLab Token Revocation API-->>+Partner API: Send new leaked credentials Partner API-->>+GitLab Public Keys endpoint: Get active public keys GitLab Public Keys endpoint-->>+Partner API: One or more public keys Partner API-->>+Partner API: Verify request is signed by GitLab Partner API-->>+Partner API: Respond to leaks Partner API-->>+GitLab Token Revocation API: HTTP status
  1. 极狐GitLab 令牌撤销 API 向合作伙伴 API 发送 (1) 撤销请求。该请求包括包含公钥标识符和请求正文签名的 header。
  2. 合作伙伴 API 请求 (2) 来自极狐GitLab 的 公钥列表。在密钥轮换的情况下,响应 (3) 可能包含多个公钥,应该使用请求 header 中的标识符进行过滤。
  3. 合作伙伴 API 针对实际请求正文,使用公钥 (4) 验证签名
  4. 合作伙伴 API 处理泄漏的令牌,可能涉及自动撤销 (5)。
  5. 合作伙伴 API 使用适当的 HTTP 状态代码响应极狐GitLab 令牌撤销 API (6):
    • 成功的响应代码(HTTP 200 到 299)确认合作伙伴已收到并处理了请求。
    • 错误代码(HTTP 400 或更高版本)导致极狐GitLab 令牌撤销 API 重试请求。

撤销请求

以下 JSON 架构文档描述了撤销请求的主体:

{
    "type": "array",
    "items": {
        "description": "A leaked token",
        "type": "object",
        "properties": {
            "type": {
                "description": "The type of token. This is vendor-specific and can be customised to suit your revocation service",
                "type": "string",
                "examples": [
                    "my_api_token"
                ]
            },
            "token": {
                "description": "The substring that was matched by the密钥Detection analyser. In most cases, this is the entire token itself",
                "type": "string",
                "examples": [
                    "XXXXXXXXXXXXXXXX"
                ]
            },
            "url": {
                "description": "The URL to the raw source file hosted on GitLab where the leaked token was detected",
                "type": "string",
                "examples": [
                    "https://gitlab.example.com/some-repo/-/raw/abcdefghijklmnop/compromisedfile1.java"
                ]
            }
        }
    }
}

示例:

[{"type": "my_api_token", "token": "XXXXXXXXXXXXXXXX", "url": "https://example.com/some-repo/-/raw/abcdefghijklmnop/compromisedfile1.java"}]

在此示例中,Secret 检测已确定 my_api_token 的一个实例已被泄露。除了包含泄漏令牌的文件的原始内容的可公开访问的 URL 之外,还会向您提供令牌的值。

请求包含两个特殊 headers:

Header 类型 描述
Gitlab-Public-Key-Identifier string 用于签署此请求的密钥对的唯一标识符。主要用于帮助密钥轮换。
Gitlab-Public-Key-Signature string 请求正文的 base64 编码签名。

您可以将这些 header 与极狐GitLab 公钥端点一起使用来验证撤销请求是否真实。

公开密钥端点

极狐GitLab 维护一个可公开访问的端点,可获取用于验证撤销请求的公钥。您可以根据要求提供端点。

以下 JSON 架构文档描述了公钥端点的响应主体:

{
    "type": "object",
    "properties": {
        "public_keys": {
            "description": "An array of public keys managed by GitLab used to sign token revocation requests.",
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "key_identifier": {
                        "description": "A unique identifier for the keypair. Match this against the value of the Gitlab-Public-Key-Identifier header",
                        "type": "string"
                    },
                    "key": {
                        "description": "The value of the public key",
                        "type": "string"
                    },
                    "is_current": {
                        "description": "Whether the key is currently active and signing new requests",
                        "type": "boolean"
                    }
                }
            }
        }
    }
}

示例:

{
    "public_keys": [
        {
            "key_identifier": "6917d7584f0fa65c8c33df5ab20f54dfb9a6e6ae",
            "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEN05/VjsBwWTUGYMpijqC5pDtoLEf\nuWz2CVZAZd5zfa/NAlSFgWRDdNRpazTARndB2+dHDtcHIVfzyVPNr2aznw==\n-----END PUBLIC KEY-----\n",
            "is_current": true
        }
    ]
}

验证请求

您可以使用从上述 API 响应中获取的相应公钥,通过针对请求正文验证 Gitlab-Public-Key-Signature header 来检查撤销请求是否真实。我们使用带有 SHA256 哈希的 ECDSA 来生成签名,然后将其 base64 编码到 header 值中。

下面的 Python 脚本演示了如何验证签名,它使用流行的 pyca/cryptography 模块进行加密操作:

import hashlib
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.hazmat.primitives.asymmetric import ec

public_key = str.encode("")      # obtained from the public keys endpoint
signature_header = ""            # obtained from the `Gitlab-Public-Key-Signature` header
request_body = str.encode(r'')   # obtained from the revocation request body

pk = load_pem_public_key(public_key)
decoded_signature = base64.b64decode(signature_header)

pk.verify(decoded_signature, request_body, ec.ECDSA(hashes.SHA256()))  # throws if unsuccessful

print("Signature verified!")

主要步骤:

  1. 将公钥加载为适合您正在使用的加密库的格式。
  2. Base64 解码 Gitlab-Public-Key-Signature header 值。
  3. 根据解码签名验证正文,指定带有 SHA256 哈希的 ECDSA。