自动响应泄露的密钥
- 版本:Ultimate
- 提供方式:GitLab.com、GitLab 自托管、GitLab 专用
GitLab 密钥检测在发现某些类型的泄露密钥时会自动响应。 自动响应可以:
- 自动撤销密钥。
- 通知签发密钥的合作伙伴。合作伙伴随后可以撤销密钥、通知其所有者,或采取其他措施防止滥用。
支持的密钥类型和操作
GitLab 支持对以下类型的密钥进行自动响应:
| 密钥类型 | 执行的操作 | 在 GitLab.com 上支持 | 在 GitLab 自托管中支持 |
|---|---|---|---|
| GitLab personal access tokens | 立即撤销令牌,向所有者发送电子邮件 | ✅ | ✅ 15.9 及更高版本 |
| Amazon Web Services (AWS) IAM access keys | 通知 AWS | ✅ | ⚙ |
| Google Cloud service account keys、API keys 和 OAuth client secrets | 通知 Google Cloud | ✅ | ⚙ |
| Postman API keys | 通知 Postman;Postman 通知密钥所有者 | ✅ | ⚙ |
组件图例
- ✅ - 默认可用
- ⚙ - 需要使用令牌撤销 API 进行手动集成
功能可用性
凭据仅在密钥检测发现它们时进行后处理:
- 在公共项目中,因为公开暴露的凭据构成更大的威胁。扩展到私有项目正在问题 391379中考虑。
- 在具有 GitLab Ultimate 的项目中,出于技术原因。扩展到所有版本在问题 391763中跟踪。
高级架构
此图表描述了后处理钩子如何在 GitLab 应用程序中撤销密钥:
%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
accTitle: 架构图
accDescr: 后处理钩子如何在 GitLab 应用程序中撤销密钥。
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)。
- 报告由服务类处理(2),如果可以撤销令牌,则安排异步工作器。
- 异步工作器(3)与外部部署的 HTTP 服务(4 和 5)通信,以确定哪些类型的密钥可以自动撤销。
- 工作器发送(6 和 7)检测到的密钥列表,GitLab 令牌撤销 API 可以撤销这些密钥。
- GitLab 令牌撤销 API 将(8 和 9)每个可撤销的令牌发送到其各自供应商的合作伙伴 API。
泄露凭据通知的合作伙伴计划
当 GitLab.com 上的公共仓库中泄露了合作伙伴签发的凭据时,GitLab 会通知合作伙伴。 如果您运营云或 SaaS 产品,并且有兴趣接收这些通知,请在史诗 4944中了解更多信息。 合作伙伴必须实现合作伙伴 API,该 API 由 GitLab 令牌撤销 API 调用。
实现合作伙伴 API
合作伙伴 API 与 GitLab 令牌撤销 API 集成,以接收和响应泄露的令牌撤销请求。该服务应该是幂等和速率限制的公开可访问的 HTTP API。
对您服务的请求可以包含一个或多个泄露的令牌,以及带有请求正文签名的标头。我们强烈建议您使用此签名验证传入请求,以证明它是来自 GitLab 的真实请求。下图详细说明了接收、验证和撤销泄露令牌的必要步骤:
%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
accTitle: 合作伙伴 API 数据流
accDescr: 合作伙伴 API 应如何接收和响应泄露的令牌撤销请求。
autonumber
GitLab Token Revocation API-->>+Partner API: 发送新的泄露凭据
Partner API-->>+GitLab Public Keys endpoint: 获取活跃的公钥
GitLab Public Keys endpoint-->>+Partner API: 一个或多个公钥
Partner API-->>+Partner API: 验证请求是否由 GitLab 签名
Partner API-->>+Partner API: 响应泄露
Partner API-->>+GitLab Token Revocation API: HTTP 状态
- GitLab 令牌撤销 API 向合作伙伴 API 发送(1)撤销请求。请求包含包含公钥标识符和请求正文签名的标头。
- 合作伙伴 API 向 GitLab 请求(2)公钥列表。响应(3)在密钥轮换的情况下可能包含多个公钥,应使用请求标头中的标识符进行过滤。
- 合作伙伴 API 使用公钥(4)验证签名与实际请求正文。
- 合作伙伴 API 处理泄露的令牌,这可能涉及自动撤销(5)。
- 合作伙伴 API 使用适当的 HTTP 状态代码(6)响应 GitLab 令牌撤销 API:
- 成功的响应代码(HTTP 200 到 299)确认合作伙伴已收到并处理了请求。
- 错误代码(HTTP 400 或更高)会导致 GitLab 令牌撤销 API 重试请求。
撤销请求
此 JSON 架构文档描述了撤销请求的正文:
{
"type": "array",
"items": {
"description": "一个泄露的令牌",
"type": "object",
"properties": {
"type": {
"description": "令牌类型。这是供应商特定的,可以根据您的撤销服务进行自定义",
"type": "string",
"examples": [
"my_api_token"
]
},
"token": {
"description": "被密钥检测分析器匹配的子字符串。在大多数情况下,这是令牌本身",
"type": "string",
"examples": [
"XXXXXXXXXXXXXXXX"
]
},
"url": {
"description": "托管在 GitLab 上的泄露令牌所在原始源文件的 URL",
"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"}]在此示例中,密钥检测已确定 my_api_token 的实例已泄露。除了包含泄露令牌的文件的原始内容的公开可访问 URL 外,令牌的值也提供给您。
请求包含两个特殊标头:
| 标头 | 类型 | 描述 |
|---|---|---|
Gitlab-Public-Key-Identifier |
string | 用于签名此请求的密钥对的唯一标识符。主要用于辅助密钥轮换。 |
Gitlab-Public-Key-Signature |
string | 请求正文的 base64 编码签名。 |
您可以使用这些标头以及 GitLab 公钥端点来验证撤销请求的真实性。
公钥端点
GitLab 维护一个公开可访问的端点,用于检索用于验证撤销请求的公钥。该端点可以根据请求提供。
此 JSON 架构文档描述了公钥端点的响应正文:
{
"type": "object",
"properties": {
"public_keys": {
"description": "由 GitLab 管理的用于签名令牌撤销请求的公钥数组。",
"type": "array",
"items": {
"type": "object",
"properties": {
"key_identifier": {
"description": "密钥对的唯一标识符。将其与 Gitlab-Public-Key-Identifier 标头的值匹配",
"type": "string"
},
"key": {
"description": "公钥的值",
"type": "string"
},
"is_current": {
"description": "密钥当前是否活跃并正在签名新请求",
"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 标头与请求正文,来检查撤销请求的真实性。我们使用带有 SHA256 哈希的 ECDSA 生成签名,然后将签名 base64 编码到标头值中。
下面的 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("") # 从公钥端点获取
signature_header = "" # 从 `Gitlab-Public-Key-Signature` 标头获取
request_body = str.encode(r'') # 从撤销请求正文获取
pk = load_pem_public_key(public_key)
decoded_signature = base64.b64decode(signature_header)
pk.verify(decoded_signature, request_body, ec.ECDSA(hashes.SHA256())) # 失败时抛出异常
print("签名验证成功!")主要步骤是:
- 将公钥加载到适合您使用的加密库的格式中。
- Base64 解码
Gitlab-Public-Key-Signature标头值。 - 使用 SHA256 哈希指定 ECDSA 来验证正文与解码的签名。