유출된 시크릿에 대한 자동 대응
GitLab v19.5Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
요약
GitLab 시크릿 탐지는 특정 유형의 유출된 시크릿을 발견하면 자동으로 대응합니다. GitLab은 다음 유형의 시크릿에 대해 자동 대응을 지원합니다: 자격 증명은 시크릿 탐지가 다음 조건에서 발견한 경우에만 후처리됩니다:
GitLab 시크릿 탐지는 특정 유형의 유출된 시크릿을 발견하면 자동으로 대응합니다. 자동 대응은 다음과 같습니다:
- 시크릿을 자동으로 취소합니다.
- 시크릿을 발급한 파트너에게 알립니다. 파트너는 시크릿을 취소하거나, 소유자에게 알리거나, 그 밖의 방법으로 악용을 방지할 수 있습니다.
지원되는 시크릿 유형 및 액션#
GitLab은 다음 유형의 시크릿에 대해 자동 대응을 지원합니다:
| 시크릿 유형 | 취해지는 액션 | GitLab.com에서 지원 | GitLab Self-Managed에서 지원 |
|---|---|---|---|
| GitLab 개인 액세스 토큰 | 즉시 토큰 취소, 소유자에게 이메일 전송.1 | ✅ | ✅ |
| Amazon Web Services(AWS) IAM 액세스 키 | AWS에 알림. | ✅ | ⚙ |
| Google Cloud 서비스 계정 키, API 키, OAuth 클라이언트 시크릿 | Google Cloud에 알림. | ✅ | ⚙ |
| Postman API 키 | Postman에 알림. Postman이 키 소유자에게 알림. | ✅ | ⚙ |
컴포넌트 범례:
- ✅ - 기본적으로 사용 가능
- ⚙ - Token Revocation API를 사용한 수동 통합 필요
기능 가용성#
자격 증명은 시크릿 탐지가 다음 조건에서 발견한 경우에만 후처리됩니다:
- 공개 프로젝트. 공개적으로 노출된 자격 증명은 위협이 더 크기 때문입니다. 비공개 프로젝트로의 확대는 이슈 391379에서 검토 중입니다.
- GitLab Ultimate를 사용하는 프로젝트. 기술적인 이유 때문입니다. 모든 티어로의 확대는 이슈 391763에서 추적 중입니다.
자동 토큰 취소 활성화#
GitLab.com에서는 자동 토큰 취소가 기본적으로 켜져 있으므로 별도의 조치가 필요하지 않습니다.
GitLab Self-Managed와 GitLab Dedicated에서는 관리자가 secret_detection_token_revocation_enabled를
true로 설정하여 활성화해야 합니다. 이 설정에는 UI가 없으며
애플리케이션 설정 API나
Rails 콘솔로 구성해야 합니다.
관리자 액세스 토큰으로 애플리케이션 설정 API를 사용합니다.
현재 값을 확인하려면 응답에서 secret_detection_token_revocation_enabled 필드를 찾습니다:
curl --header "PRIVATE-TOKEN: <your_admin_access_token>" \
--url "https://gitlab.example.com/api/v4/application/settings"
이 설정을 활성화하려면:
curl --request PUT \
--header "PRIVATE-TOKEN: <your_admin_access_token>" \
--data "secret_detection_token_revocation_enabled=true" \
--url "https://gitlab.example.com/api/v4/application/settings"
Rails 콘솔 세션을 엽니다.
현재 값을 확인하려면:
::Gitlab::CurrentSettings.secret_detection_token_revocation_enabled?
설정을 활성화하려면:
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_enabled: true)
고수준 아키텍처#
다음 다이어그램은 후처리 훅이 GitLab 애플리케이션에서 시크릿을 취소하는 과정을 보여 줍니다:
소스 코드 보기
%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
accTitle: Architecture diagram
accDescr: How a post-processing hook revokes a secret in the GitLab application.
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</code></pre></details></div>
- 시크릿 탐지 job이 포함된 파이프라인이 완료되어 스캔 보고서를 생성합니다(1).
- 서비스 클래스가 보고서를 처리하고(2), 토큰 취소가 가능하면 비동기 워커를 예약합니다.
- 비동기 워커(3)가 외부에 배포된 HTTP 서비스와 통신하여
(4, 5) 어떤 종류의 시크릿을 자동으로 취소할 수 있는지 판단합니다.
- 워커는 GitLab Token Revocation API가 취소할 수 있는 탐지된 시크릿 목록을
전송합니다(6, 7).
- GitLab 토큰 취소 API는 취소 가능한 각 토큰을 해당 벤더의 파트너 API로 전송합니다(8, 9).
유출된 자격 증명 알림을 위한 파트너 프로그램#
GitLab은 파트너가 발급한 자격 증명이 GitLab.com의 공개 리포지터리에서 유출되면 해당 파트너에게 알립니다.
클라우드 또는 SaaS 제품을 운영하며 이 알림을 받고자 한다면 에픽 4944에서 자세한 내용을 확인할 수 있습니다.
파트너는 GitLab 토큰 취소 API가 호출하는 파트너 API를 구현해야 합니다.
파트너 API 구현#
파트너 API는 GitLab 토큰 취소 API와 통합되어 유출 토큰 취소 요청을 수신하고 응답합니다.
이 서비스는 멱등성을 갖추고 속도 제한이 적용된, 공개적으로 접근 가능한 HTTP API여야 합니다.
서비스로 오는 요청에는 유출된 토큰이 하나 이상 포함될 수 있으며, 요청 본문의 서명이 담긴 헤더가 함께 전달됩니다.
이 서명으로 수신 요청을 검증하여 GitLab이 보낸 진짜 요청인지 확인할 것을 강력히 권장합니다.
다음 다이어그램은 유출된 토큰을 수신, 검증, 취소하는 데 필요한 단계를 보여 줍니다:
Mermaid 다이어그램 (12줄)소스 코드 보기
%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
accTitle: Partner API data flow
accDescr: How a partner API should receive and respond to leaked token revocation requests.
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</code></pre></details></div>
- GitLab 토큰 취소 API가 파트너 API로 취소 요청을 보냅니다(1). 이 요청에는
공개 키 식별자와 요청 본문의 서명이 담긴 헤더가 포함됩니다.
- 파트너 API는 GitLab에 공개 키 목록을 요청합니다(2). 응답(3)에는
키 순환 시 여러 공개 키가 포함될 수 있으므로 요청 헤더의 식별자로 필터링해야 합니다.
- 파트너 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 customized to suit your revocation service",
"type": "string",
"examples": [
"my_api_token"
]
},
"token": {
"description": "The substring that was matched by the secret detection analyzer. 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"}]
이 예시에서 시크릿 탐지는 my_api_token 인스턴스가 유출되었다고 판단했습니다. 토큰
값과 함께, 유출된 토큰이 포함된 파일의 원본 콘텐츠에 접근할 수 있는 공개 URL이
제공됩니다.
이 요청에는 두 가지 특수 헤더가 포함됩니다:
헤더
유형
설명
Gitlab-Public-Key-Identifier
string
이 요청에 서명하는 데 사용된 키 쌍의 고유 식별자. 주로 키 순환을 돕는 데 사용됩니다.
Gitlab-Public-Key-Signature
string
요청 본문의 base64 인코딩된 서명.
이 헤더와 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 헤더를
검증하여 취소 요청이 진짜인지 확인할 수 있습니다. GitLab은 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("") # 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!")
주요 단계는 다음과 같습니다:
- 사용하는 암호화 라이브러리에 맞는 형식으로 공개 키를 로드합니다.
Gitlab-Public-Key-Signature 헤더 값을 base64로 디코딩합니다.
- SHA256 해싱을 사용하는 ECDSA를 지정하여 디코딩된 서명에 대해 본문을 검증합니다.
Footnotes