泄露 secrets 时的自动响应
引入于极狐GitLab 13.6。
极狐GitLab Secret 检测在发现某些类型的泄露机密时会自动响应,可以:
- 自动撤销 secret。
- 通知发布 secret 的合作伙伴。然后合作伙伴可以撤销 secret,通知其所有者,或以其他方式防止滥用。
支持的 secret 类型和操作
极狐GitLab 自动响应支持以下类型的 secret:
Secret 类型 | 执行的操作 | SaaS | 私有化部署 |
---|---|---|---|
极狐GitLab 个人访问令牌 | 立即撤销令牌,发送电子邮件给所有者 | ✅ | ✅ (15.9 及更高版本) |
Amazon Web Services (AWS) IAM 访问密钥 | 通知 AWS | ✅ | ⚙ |
说明
- ✅ - 默认可用
- ⚙ - 需要使用令牌撤销 API 进行手动集成
可用的功能
为非默认分支启用于 15.11 版本。
凭据仅在 Secret 检测发现它们时才进行后处理:
- 支持公开项目,因为公开暴露的凭据构成了更大的威胁。
- 支持旗舰版项目。
高层架构
下图描述了后处理钩子如何撤销极狐GitLab 应用程序中的 secret:
- 带有 Secret 检测作业的流水线完成,生成扫描报告 (1)。
- 该报告由一个服务类处理 (2),如果可以撤销令牌,该服务类会安排一个异步 worker。
- 异步 worker (3) 与外部部署的 HTTP 服务(4 和 5)通信,来确定可以自动撤销哪些类型的 secret。
- Worker 发送(6 和 7)极狐GitLab 令牌撤销 API 能够撤销的检测到的 secret 列表。
- 极狐GitLab 令牌撤销 API 将(8 和 9)每个可撤销令牌发送到各自供应商的 Partner API。
泄露凭据通知的合作伙伴计划
当 SaaS 上的公开仓库中泄露他们颁发的凭据时,极狐GitLab 会通知合作伙伴。合作伙伴必须实施合作伙伴 API,其由极狐GitLab 令牌撤销 API 调用。
实施合作伙伴 API
合作伙伴 API 与极狐GitLab 令牌撤销 API 集成,来接收和响应泄漏的令牌撤销请求。该服务应该是一个可公开访问的 HTTP API,具有幂等性和速率限制。
对您的服务的请求可以包含一个或多个泄露的令牌,以及带有请求正文签名的 header,建议您使用此签名验证传入请求,以证明它是来自极狐GitLab 的真实请求。下图详细说明了接收、验证和撤销泄漏令牌的必要步骤:
- 极狐GitLab 令牌撤销 API 向合作伙伴 API 发送 (1) 撤销请求。该请求包括包含公钥标识符和请求正文签名的 header。
- 合作伙伴 API 请求 (2) 来自极狐GitLab 的 公钥列表。在密钥轮换的情况下,响应 (3) 可能包含多个公钥,应该使用请求 header 中的标识符进行过滤。
- 合作伙伴 API 针对实际请求正文,使用公钥 (4) 验证签名。
- 合作伙伴 API 处理泄漏的令牌,可能涉及自动撤销 (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 Secret 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!")
主要步骤:
- 将公钥加载为适合您正在使用的加密库的格式。
- Base64 解码
Gitlab-Public-Key-Signature
header 值。 - 根据解码签名验证正文,指定带有 SHA256 哈希的 ECDSA。