취약점 추적 개요
GitLab v19.1GitLab에서는 Git과 자동화된 보안 테스팅을 Continuous Integration and Continuous Delivery(CI/CD) 프로세스와 결합하여 실행합니다. 코드 변동성(프로젝트 소스 코드의 지속적인 변경)과 이중 보고(여러 도구에서 보고된 결과물의 중복)는 중복의 잠재적 원인으로, 분석가에게 불필요한 감사 부담을 줍니다.
GitLab에서는 Git과 자동화된 보안 테스팅을 Continuous Integration and Continuous Delivery(CI/CD) 프로세스와 결합하여 실행합니다. 이 프로세스는 코드 변경 사항을 지속적으로 모니터링하여 보안 취약점을 최대한 조기에 탐지합니다. 보안 테스팅에는 주로 여러 Static Application Security Testing(SAST) 도구가 사용되며, 각 도구는 하드코딩된 비밀번호나 불안전한 데이터 흐름과 같은 특정 취약점 탐지에 특화되어 있습니다. 여러 도구를 조합한 이기종(heterogeneous) SAST 설정은 소프트웨어의 공격 표면을 최소화하는 데 도움이 됩니다. 이러한 도구들로부터 얻은 보안 결과물(findings)은 취약점 관리(Vulnerability Management)라는 반자동 프로세스를 통해 이해, 분류, 저장, 처리됩니다.
코드 변동성(프로젝트 소스 코드의 지속적인 변경)과 이중 보고(여러 도구에서 보고된 결과물의 중복)는 중복의 잠재적 원인으로, 분석가에게 불필요한 감사 부담을 줍니다.
취약점 추적은 소프트웨어 프로젝트의 생애 주기 전반에 걸쳐 취약점을 중복 제거하고 추적하는 데 도움을 주는 자동화된 프로세스입니다.
GitLab의 취약점 추적 방법은 Scope+Offset(내부)을 기반으로 합니다.
Scope+Offset 방법의 전신은 라인 기반 핑거프린팅(line-based fingerprinting)으로, 이 방법은 더 취약하여 이미 탐지된 취약점이 재도입되는 사례가 많았습니다.
중복 방지가 Scope+Offset 방법을 구현하게 된 동기였습니다.
관련 연구 이슈에서 더 많은 배경 정보를 확인하세요(내부).
컴포넌트#
매우 높은 수준에서 취약점 추적 흐름은 아래와 같이 묘사됩니다. 이 섹션의 나머지 부분에서는, 취약점 추적 목적상 SAST 분석기와 Tracking Calculator가 추적 서명(tracking signature) 생산자(producer) 컴포넌트를 나타내고, Rails 백엔드가 추적 서명 소비자(consumer) 컴포넌트를 나타낸다고 가정합니다. 각 컴포넌트에 대한 자세한 설명은 아래에 있습니다.
flowchart LR R["Repository"] S("SAST Analyzer [CI]") T("tracking-calculator [CI]") B("Rails backend")
R --code--> S --gl-sast-report.json--> T --augmented gl-sast-report.json--> B R --code --> T
추적 서명 생산자#
SAST 분석기는 CI 컨텍스트에서 실행되며, 소스 코드를 분석하여 gl-sast-report.json 파일을 생성합니다.
Tracking Calculator는 소스 코드를 기반으로 스코프(scope)를 계산하고, 이를 gl-sast-report.json에 나열된 취약점과 매칭합니다.
매칭이 이루어지면, Tracking Calculator는 Scope+Offset 방식으로 서명(signature)을 계산하고, 아래에 묘사된 tracking 객체를 통해 원본 보고서에 각각을 포함시켜 gl-sast-report를 보강(augment)합니다.
"tracking": {
"type": "source",
"items": [
{
"file": "test.c",
"line_start": 12,
"line_end": 12,
"signatures": [
{
"algorithm": "scope_offset_compressed",
"value": "test.c|main()[0]:5"
},
{
"algorithm": "scope_offset",
"value": "test.c|main()[0]:8"
}
]
}
]
}
Tracking Calculator는 SAST 분석기의 Docker 이미지(내부)에 직접 내장되어 있으며, 이 스크립트를 통해 호출됩니다.
Tracking Calculator는 이미 기본적으로 활성화된 중복 제거를 수행합니다.
위 예시에서 scope_offset_compressed와 scope_offset라는 두 가지 알고리즘이 있는데,
scope_offset_compressed는 scope_offset의 개선된 버전으로 간주되어 더 높은 우선순위가 부여됩니다.
scope_offset과 scope_offset_compressed가 동일한 핑거프린트에 동의할 경우,
더 높은 우선순위를 가진 알고리즘으로 간주되는 scope_offset_compressed의 결과만 추가됩니다.
그런 다음 보고서는 소비자 컴포넌트로 수집(ingest)되며, 이 서명들은 취약점 UUID를 통해 취약점 핑거프린트를 생성하는 데 사용됩니다.
추적 서명 소비자#
Rails 코드에서는 보안 결과물(보고서에서 발생한 결과물)과 취약점 결과물(데이터베이스(DB)에 영구 저장된 것)을 구분합니다. 보안 결과물은 보고서가 파싱될 때 생성되며, 이 곳이 UUID가 생성되는 위치이기도 합니다.
보안 결과물 임시 저장#
아래 다이어그램은 모든 파이프라인에서 보안 결과물을 임시로 저장하기 위해 실행되는 흐름을 묘사합니다.
취약점 추적 관점에서 가장 흥미로운 컴포넌트 중 하나는 OverrideUuidsService입니다.
OverrideUuidsService는 서명 레벨에서 보안 결과물과 취약점 결과물을 매칭합니다.
매칭이 있으면, 보안 결과물의 UUID가 그에 맞게 덮어씌워집니다.
StoreFindingsService는 재조정된 결과물을 security_findings 테이블에 저장합니다.
보안 보고서로부터 취약점이 생성되는 방법에 대한 자세한 문서는 보안 보고서로부터의 취약점 생성에서 확인할 수 있습니다.
소스 코드 참조:
sequenceDiagram
Producer->>Sidekiq: gl-sast-report.json
Sidekiq->>StoreScansWorker: <
loop for every artifact in artifacts
StoreGroupedScansService->>StoreScanService: artifact
StoreScanService->>OverrideUuidsService: security-report
StoreScanService->>StoreFindingsService: store findings
end
end
시나리오 2: 머지 리퀘스트 보안 위젯#
두 번째 시나리오는 머지 리퀘스트 보안 위젯과 관련됩니다.
소스 코드 참조:
SecurityFindingsReportsComparer는 새로 추가되거나 수정된 결과물의 수를 계산합니다.
먼저 기본 브랜치와 비기본 브랜치 간의 보안 결과물을 비교하여 추가 및 수정된 결과물의 수를 계산합니다.
sequenceDiagram MergeRequestModel->>CompareSecurityReportsService: compare_sast_reports CompareSecurityReportsService->>SecurityFindingsReportsComparer: calculate_changes
시나리오 3: 보고서 수집#
이 단계에서 보안 결과물이 취약점이 되거나, 보안 결과물에 해당하는 취약점이 업데이트됩니다.
이 시나리오는 비기본 브랜치를 기본 브랜치에 병합할 때 기본 브랜치에서 트리거된 파이프라인과 관련이 있을 때 해당됩니다.
우리의 맥락에서는, 이미 존재하는 취약점과의 충돌이 있음을 의미하는 overridden_uuid가 설정된 보안 결과물이 있는 경우에 가장 관심이 있습니다.
overridden_uuid는 해당 취약점 UUID에 의해 덮어씌워진 보안 결과물의 UUID를 보유합니다.
아래 시퀀스는 취약점의 UUID(핑거프린트)를 업데이트하기 위해 실행됩니다.
재계산은 UpdateVulnerabilityUuids에서 이루어지며, 궁극적으로 UpdateVulnerabilityUuidsVulnerabilityFinding 클래스를 통해 데이터베이스 업데이트를 호출합니다.
소스 코드 참조:
sequenceDiagram IngestReportsService->>IngestReportService: security_scan IngestReportService->>IngestReportSliceService: sliced security_scan IngestReportSliceService->>UpdateVulnerabilityUuids: findings map
계층 구조: 알고리즘에 우선순위가 부여되는 이유와 이 우선순위가 미치는 영향#
지원되는 알고리즘은 VulnerabilityFindingSignatureHelpers에 정의되어 있습니다.
알고리즘에는 우선순위(아래 맵의 정수 값)가 부여됩니다.
높은 우선순위는 해당 알고리즘이 낮은 우선순위 알고리즘보다 더 낫다는 것을 의미합니다.
다시 말해, 낮은 우선순위에서 높은 우선순위 알고리즘으로 전환하는 것은 coarsening(더 나은 중복 제거 성능)에 해당하며, 높은 우선순위에서 낮은 우선순위 알고리즘으로 전환하는 것은 refinement(약한 중복 제거 성능)에 해당합니다.
ALGORITHM_TYPES = {
hash: 1,
location: 2,
scope_offset: 3,
scope_offset_compressed: 4,
rule_value: 5
}.with_indifferent_access.freeze