주요 내용
- Go는 패키지를 특정 사용자가 소유한 이름이 아니라 패키지가 위치한 곳을 기준으로 식별하기 때문에, GitHub 사용자 이름이 만료된 것만으로도 그 자체로 공급망 문제가 될 수 있습니다.
- 체크섬 데이터베이스는 특정 버전에 대해 모든 사용자가 동일한 코드를 받도록 보장할 뿐, 해당 버전이 신뢰할 수 있는 출처에서 제공된 것임을 보장하는 것은 아닙니다.
- GitHub는 인기 있는 네임스페이스를 단계적으로 폐지하고 있지만, Go의 모듈 미러를 통해 제공되는 다운로드는 해당 보호 기능을 발동시키는 클론으로 인식되지 않습니다.
- 검토가 필요한 경로는 아무도 읽지 않는 사소한 전이적 경로들입니다.
2023년 12월, VulnCheck의 연구원들은 GitHub에서 회수 가능한 상태에 있는 Go 저장소를 찾아보았는데, 그 수가 대부분의 사람들이 예상했던 것보다 훨씬 더 많았습니다. 15,000개가 넘었습니다. 9,000개 이상은 GitHub 사용자 이름 변경으로 인해 노출되었으며, 6,000개 이상은 계정 삭제로 인해 노출된 것이었습니다. 이 리포지토리들을 합치면 80만 개 이상의 Go 모듈 버전을 포함하고 있습니다.
이 모든 과정에 도난당한 인증 정보나 해킹당한 빌드 파이프라인은 전혀 필요하지 않습니다. 단지 계정이 만료되고, 원래 소유자보다 먼저 누군가가 이를 알아차리기만 하면 됩니다.
그 통계는 이제 몇 년 전의 것이며, 아직까지 새로운 통계를 발표한 사람은 없습니다. 하지만 그 원리는 변하지 않았으며, Go에서는 효과가 있지만 다른 언어에서는 거의 효과가 없는 이유는 Go가 ‘패키지’를 어떻게 정의하느냐에 달려 있습니다.
왜 Go 패키지 식별자가 이름이 아닌 위치로 간주되는가
대부분의 주요 레지스트리는 소유권 기록을 보관합니다. npm은 누가 axios를 공개할 수 있는지 파악하고 있습니다. PyPI는 누가 requests의 새 릴리스를 푸시할 수 있는지 파악하고 있습니다. Maven Central은 누군가가 특정 네임스페이스 아래의 groupId를 신청하기 전에 해당 네임스페이스를 검증합니다.
Go 언어에는 주소 역할을 겸하는 경로가 있습니다.
github.com/someuser/somelib은 Go 레지스트리에 등록된 이름이 아닙니다. 이는 툴체인에 어디를 찾아야 할지 알려주는 모듈 경로이며, 해당 위치를 관리하는 사람이 향후 그곳에서 어떤 버전을 제공할지 결정합니다. 이 계정 소유자가 사라지고 다른 사람이 해당 사용자 이름을 차지하면, go.mod 파일의 임포트 경로는 이제 그 사람의 코드를 가리키게 됩니다.
이는 최근 헤드라인을 장식하고 있는, 다음과 같은 계정 해킹 캠페인과는 전혀 다른 성격의 것입니다. PolinRider과 같은 최근 헤드라인을 장식한 계정 탈취 캠페인과는 성격이 전혀 다릅니다. 해당 캠페인 역시 2026년에 Go 모듈을 노렸지만, 그 수법은 다릅니다. 그 공격자들은 여전히 활성 상태였던 계정을 장악했고, 몇몇 사례에서는 자신의 행적을 숨기기 위해 Git 태그를 수정하기도 했습니다. 리포지토리 탈취(Repojacking)는 더 은밀하며, 어떤 면에서는 더 쉬운 방식입니다. 침입할 대상이 필요하지 않기 때문입니다. 일단 계정이 사라지면 방어할 대상 자체가 없어지기 때문입니다.
Go의 모듈 해결 방식은 우아하며, 이 점에 대해 먼저 언급한 뒤 문제점이 무엇인지 살펴볼 필요가 있습니다. `go get github.com/user/lib` 명령어는 기본적으로 모듈 프록시를 통해 모듈을 해결하고, 버전을 기록한 뒤 해시를 작성합니다. 계정 등록도, 레지스트리 제출도, 게시 파이프라인도 필요하지 않습니다. 적절하게 이름이 지정된 버전 태그를 푸시하기만 하면 버전이 생성되며, 그 사이에 별도의 게시 단계가 필요하지 않습니다.
이 방식이 통하는 이유는 임포트 경로가 ‘진실의 원천’이기 때문입니다. 이 경로는 툴체인에 어디를 찾아야 할지 알려주며, 그곳에 코드를 올릴 수 있는 권한이 있는 사람을 기록하는 별도의 시스템은 존재하지 않습니다.
각 레지스트리가 패키지에 대해 실제로 어떤 정보를 가지고 있는지 비교해 보세요.
| 레지스트리 | 정체성이란 | 소유권 기록 | 포기 후 회수 가능 |
| npm | 등록된 이름 | 네, 계정과 연동되어 있습니다. | 아니요, 이름은 계속 예약된 상태로 유지됩니다. |
| PyPI | 등록된 이름 | 네, 계정과 연동되어 있습니다. | 오직 공식적인 절차를 통해서만 |
| Maven Central | 검증된 도메인 또는 소스 저장소에 연결된 네임스페이스 | 네, 네임스페이스 확인됨 | 네, 도메인 등록 기간이 만료된 경우라면 |
| crates.io | 등록된 이름 | 네, 계정과 연동되어 있습니다. | 아니요, 이름은 계속 예약된 상태로 유지됩니다. |
| Packagist | 등록된 이름 | 네, 계정과 연동되어 있습니다. | 아니요, 이름은 계속 예약된 상태로 유지됩니다. |
| Go 모듈 | VCS 위치 | 없음 | 네, 그 장소가 비게 될 때마다 |
Maven 역시 만료된 도메인로 인해 이 문제와 유사한 사안을 겪고 있으며, 이는 2024년 1월에 발표된 ‘MavenGate’ 연구에서 다룬 내용이기도 합니다. 그룹 ID는 도메인에서 파생되므로, 도메인이 만료되면 해당 도메인에 연결된 네임스페이스도 함께 양도됩니다.
Packagist에도 기록된 사례가 있습니다. 2022년 발생한 사건에서, 한 연구원이 Packagist 패키지의 GitHub 저장소를 탈취하여 악성코드가 배포된 사례가 있었습니다. 이는 Packagist가 별도의 배포 단계 없이 GitHub에서 릴리스를 크롤링하고 있었기 때문이었습니다. 그 이후로 해당 취약점은 해결되었으며, 2026년 5월 발생한 Laravel-Lang 태그 재작성 사건 이후, Packagist는 한 걸음 더 나아가 안정 버전을 불변으로 만들었습니다. 이는 5월에 발표되어 7월부터 적용되었습니다. 다만, 이 조치가 무엇을 해결하는지 명확히 할 필요가 있습니다. 불변 버전은 누군가가 게시된 버전의 Git 참조를 변경하는 것을 막을 뿐입니다. 누군가가 버려진 네임스페이스를 차지하고 그 아래에 새로운 내용을 게시하는 것에 대해서는 아무런 조치도 취하지 않습니다.
Go의 경우, 두 번째 시나리오는 개인 GitHub 계정에 호스팅된 모든 모듈에 대해 기본 설정으로 적용됩니다. GitHub의 보안 팀은 2024년 ‘repojacking’ 관련 게시물에서 GitHub Actions 및 Git 서브모듈과 함께 세 가지 방법 중 하나로 꼽았으며, 이 두 가지에 대해서는 특정 커밋에 고정할 것을 권고했습니다. Go는 go.sum 및 기본적으로 공개 체크섬 데이터베이스와 대조하여 모듈 버전을 인증하기 때문에, GitHub Actions나 Git 서브모듈에는 없는 무결성 계층을 갖추고 있습니다. 바로 이 점이 이 문제를 처음 보았을 때보다 훨씬 더 흥미롭게 만드는 부분입니다.
이 위험에는 서서히 드러나는 형태도 있습니다. 운영 코드 어딘가에 포함된 오픈소스 구성 요소 중 상당수는 수년 동안 커밋이 이루어지지 않았으며, 활동이 중단된 프로젝트야말로 유지보수자가 결국 계정을 삭제하거나 도메인 등록을 갱신하지 않는 경우가 많습니다. 대부분의 생태계에서 이는 패치 문제이지만, Go의 경우 이는 또한 해킹의 표적이 될 수 있는 부분이기도 합니다.
임포트 경로를 대상으로 한 리포재킹의 작동 원리
두 가지 계정 수준 이벤트가 GitHub 경로를 해제합니다. 계정을 삭제하면 사용자 이름이 해제되고, 사용자 이름을 변경하면 기존 사용자 이름이 해제됩니다. 두 경우 모두 github.com/olduser/lib는 미등록 상태로 남게 되며, 다음에 해당 사용자 이름을 등록하는 사람은 동일한 이름의 저장소를 다시 생성하여 해당 경로를 소유할 수 있습니다. 아직 존재하는 계정 아래의 단일 저장소를 삭제하는 것만으로는 이러한 효과가 발생하지 않습니다. 다른 사람이 귀하의 계정 내에서 저장소를 생성할 수 없기 때문입니다.
GitHub은 이에 대한 방어 조치를 취하고 있지만, 그 범위는 대부분의 사람들이 생각하는 것보다 좁습니다. 네임스페이스 폐기(namespace retirement)는 소유자와 저장소 이름의 조합을 영구적으로 잠가 재사용할 수 없게 하며, 이는 지난 주 동안 100개 이상의 클론이 있거나, 이름 변경 또는 삭제 전 주 동안 이름이 변경되거나 삭제되기 전 1주일 동안 클론 수가 100개 이상이거나 GitHub Actions 사용 횟수가 100회 이상이었던 저장소, 그리고 GitHub Marketplace에 등재된 액션이 포함된 모든 저장소를 대상으로 합니다.
즉, 이 보호 조치는 모두가 이미 우려하고 있는 패키지들만 포함하고, 롱테일 부분은 제외된다는 뜻입니다.
특히 Go의 경우, 그 ‘롱 테일’이 엄청나게 큰 것으로 드러났으며, 15,000개 이상의 저장소를 확인한 바로 그 연구에서 그 이유도 밝혀냈습니다. Go의 모듈 미러는 저장소를 한 번 가져온 뒤, 그 사본을 다른 모든 사용자에게 제공합니다. 한 모듈은 수천 건의 `go get` 호출의 대상이 될 수 있지만, 이러한 프록시를 통해 제공되는 다운로드가 GitHub에서 클론으로 기록되지는 않기 때문에, 많이 사용되는 라이브러리라도 네임스페이스를 보호하기 위한 클론 기준치 미만에 머물 수 있습니다. GitHub Action이 아니며 마켓플레이스에 등록되지 않은 일반적인 Go 라이브러리의 경우, 클론 수는 다운스트림 사용량을 심각하게 과소평가하게 됩니다.
주당 40개의 클론이 생성되고, 이를 통해 수백 개의 프로젝트에 간접적으로 포함되는 유틸리티 라이브러리는 어쨌든 기준치에 훨씬 미치지 못합니다. 바로 이러한 경로들이 실제로 감사가 필요한 부분이지만, 다른 사람의 의존성 트리에서 4단계나 깊숙이 위치해 있기 때문에 아무도 살펴보지 않는 경로이기도 합니다.
폐지 여부는 지속적으로 평가되는 것이 아니라, 계정 이름이 변경되거나 삭제되는 시점에 평가됩니다. 2년 전에는 인기가 많았지만 그 이후로 활동이 뜸해진 저장소는, 해당 주에 기준치를 충족하지 못했을 수도 있습니다. 이를 확인할 수 있는 API나 공개 목록은 없으므로, 저장소를 생성해 보고 GitHub에서 해당 이름이 이미 폐지되어 재사용할 수 없다는 응답이 돌아오는지 확인하는 것이 유일한 방법입니다.
체크섬 데이터베이스가 새 버전을 보증할 수 없는 이유
Go의 체크섬 인프라가 정말 훌륭하며, 대부분의 생태계가 갖춘 것보다 뛰어납니다. 이 인프라가 정확히 무엇을 보장하는지, 그리고 그 보장이 어디까지인지 정확히 아는 것이, 단순히 ‘보장받고 있다고 느끼는 것’과 ‘실제로 보장받는 것’의 차이를 만듭니다.
sum.golang.org 는 모듈 해시에 대한 추가 전용이며 공개적으로 감사 가능한 기록을 보관합니다. 빌드 과정에서 go.sum에 아직 등록되지 않은 공개 모듈 버전을 식별하면, 툴체인은 해당 해시를 로그에서 조회한 후 로컬에 기록하므로, 첫 번째 다운로드 시 검증되지 않은 상태로 처리되는 일은 없습니다. 이후 빌드에서는 이미 파일에 저장된 내용과 비교하며, `go mod verify` 명령어는 로컬 모듈 캐시에 있는 복사본이 다운로드된 이후로 수정되지 않았는지 확인합니다.
그 모든 시스템은 한 가지 일을 매우 잘 수행합니다. 바로 example.com/[email protected]를 요청하는 모든 사용자에게 오늘이든 5년 후든 똑같은 바이트를 제공하도록 보장하는 것입니다. 이미 기록된 버전으로 다른 콘텐츠를 다시 게시하면, 그 불일치가 뚜렷하게 드러납니다.
리포재킹은 기록된 버전을 절대 건드리지 않습니다. 공격자는 완전히 새로운 버전이나 전혀 다른 경로를 공개하며, 체크섬 데이터베이스는 원래 의도된 대로 정확히 작동합니다. 진정으로 새로운 버전의 경우, 공격자가 제공하는 콘텐츠가 해당 모듈과 버전에 대한 기록된 콘텐츠가 될 수 있으며, 이후부터 그 아래에 있는 모든 사용자에게는 이 콘텐츠가 정확히 인증된 것으로 인식됩니다. 해시는 진짜입니다. 로그 항목도 진짜입니다. 이 둘 중 어느 곳에도 해당 코드가 원래 신뢰했던 유지보수자가 제공한 것임을 나타내는 내용은 없습니다.
| 시나리오 | 해당 버전에 대한 이전 해시 | 인수 시도 감지됨 |
| 새로운 내용을 추가하여 개정판으로 재출간된 녹음본 | 예 | 네, 체크섬이 일치하지 않습니다. |
| 탈취된 경로의 새로운 소버전 | 아니요, 완전히 새로운 버전입니다. | 아니요 |
| 인수합병 후 새로운 종속기업이 추가됨 | 아니요 | 아니요 |
| 전이적 종속성이 인수 지점을 넘어섰다 | 아니요, 완전히 새로운 버전입니다. | 아니요 |
의존성을 업데이트하거나 새로운 의존성을 추가하면, 툴체인은 해당 바이트가 다른 모든 사람이 받고 있는 바이트와 동일하다는 것을 증명할 수 있습니다. 하지만 이것이 해당 바이트가 여러분의 빌드에 속한다는 것을 증명하는 것과는 다릅니다.
proxy.golang.org에 있는 모듈 프록시는 여기에서 어느 정도 도움이 됩니다. 이 미러는 이미 가져온 버전을 보관하고 있으며, 업스트림에서 해당 버전이 사라진 후에도 계속 제공할 수 있습니다. 따라서 인수 이전에 가져온 버전은 나중에 저장소가 삭제되거나 대체되더라도 변경되지 않은 상태로 계속 사용할 수 있습니다. 이는 실질적인 보안 특성입니다. 다만 이 특성은 인수 이전에 이미 캐시된 버전에만 적용되며, 다음 버전에는 아무런 영향을 미치지 않습니다.
남은 것은 어떤 체크섬으로도 답할 수 없는 질문, 즉 첫 번째 해결 단계에서 나타나는 콘텐츠가 과연 신뢰할 만한 것인지 여부입니다. 모듈이 해결되기 전에 이를 심사하고, 소스 코드부터 재컴파일하여 경로를 통해 제공된 내용과 비교하는 것은 일련의 과정 중 더 이른 단계에서 이루어지는 별개의 제어 방식입니다. 이것이 바로 Go용 SecureChain 가 프록시 앞에 추가하는 계층입니다.
‘Vanity’ 임포트 경로, 두 번째 경로 개척
GitHub 사용자 이름을 노리는 ‘리포잭킹’은 이름이 붙여지고 관련 연구 결과까지 나온 사례입니다. 반면, 훨씬 덜 주목받는 유형은 사용자 지정 도메인을 이용한 것입니다.
Go는 HTML 메타 태그를 통해 임의의 임포트 경로를 지원합니다. example.com/mylib을 조회하면 툴체인이 https://example.com/mylib?go-get=1으로 이동하게 되며, 이곳에서는 실제 VCS 위치를 명시하는 go-import 메타 태그를 반환하고, 툴체인은 이를 따라갑니다. 이것이 바로 golang.org/x/…이 작동하는 방식이며, 기업이 자체 도메인 아래에서 모듈을 공개하는 방식이기도 합니다.
이 취약점의 발생 방식은 설명할 필요도 없을 정도인데, 주로 공격자가 별다른 수법을 쓸 필요가 없기 때문입니다. 도메인이 만료되면, 그 도메인을 다음에 등록하는 사람이 해당 도메인 아래의 모든 모듈 경로를 통제하게 되며, 이 경로들을 원하는 곳 어디로든 연결할 수 있게 됩니다. 여기에는 클론 수 제한도 없고, 네임스페이스 폐기 절차도 없으며, GitHub의 개입도 전혀 없습니다.
소규모 컨설팅 업체, 폐업한 스타트업, 1인 오픈소스 프로젝트 등—바로 이런 것들이 노출 대상입니다. 또한, 이력이 남아 있는 GitHub 사용자 이름보다 만료된 도메인을 확보하는 편이 비용도 더 저렴하고 눈에 덜 띕니다.
go list -m -f ‘{{.Path}}’ all \
| grep -v ‘^github.com/\|^golang.org/\|^gopkg.in/’ \
| cut -d/ -f1 | sort -u
이렇게 하면 빌드 과정에서 해결되는 모든 비명백한 호스트를 확인할 수 있습니다. 각 도메인을 누가 관리하는지 파악하고 등록 상태를 주시해야 합니다. 만료가 임박했거나 단순히 방치된 것처럼 보이는 도메인은 기한이 정해진 종속성이기 때문입니다.
모듈 경로 검토를 통한 인수 위험 파악
두 가지 확인 절차로, 둘 다 간단합니다. 모두가 이미 주시하고 있는 주력 서비스뿐만 아니라, 조직이 보유한 모든 Go 저장소에 대해 이 두 가지를 모두 적용해 보세요.
먼저, 더 이상 활성 저장소 응답을 반환하지 않는 GitHub 저장소 URL을 표시하는 것부터 시작하세요.
go list -m -f ‘{{.Path}}’ all | grep ‘^github.com/’ \
| cut -d/ -f1-3 | sort -u \
| while read p; do
printf ‘%s %s\n’ “$(curl -s -o /dev/null -w ‘%{http_code}’ “https://$p”)” “$p”
done | grep -v ‘^200’
404 오류는 해당 경로가 더 이상 공개적으로 접근할 수 없음을 나타내는 매우 중요한 신호입니다. 리포지토리가 비공개로 전환되었을 가능성도 있으므로, 이를 확실한 리포지토리 탈취 위험으로 간주하기 전에 먼저 조사해 볼 필요가 있습니다. 301 오류는 대개 이름 변경이나 계정 이전을 의미하며, 이 검사는 의도적으로 리디렉션을 따르지 않으므로 표시된 상태 그대로를 확인할 수 있습니다. 공격자가 경로를 탈취하면 검사 결과는 다시 200으로 돌아가므로, 이 검사는 탈취가 발생한 후가 아니라 그 직전의 시점을 포착합니다.
그렇다면, 결국 해결되는 길들 중에서 뒤처진 것이 무엇인지 살펴보자.
go list -m -u all | grep ‘\[‘
이는 더 새로운 버전이 있는 모듈을 표시해 주는 것으로, 정답이라기보다는 출발점에 가깝습니다. 업스트림에서 계속해서 새 버전을 출시하고 있는 상황에서 몇 버전이나 뒤처진 모듈의 경우, 해당 저장소의 실제 활동 상황을 함께 확인해 볼 필요가 있습니다.
404 오류 확인 작업을 일회성 감사로 취급하지 말고, CI에서 매주 정기적으로 수행하도록 설정하세요. 오늘 해당 리포지토리 중 하나를 소유하고 있는 계정이 다음 달에 삭제되거나 이름이 변경될 수 있으며, 누군가가 그 이전 사용자 이름을 차지하기 전까지의 기간이 바로 여러분이 얻을 수 있는 무료 경고 기간입니다.
업스트림이 영구적으로 사라진 모듈에 대해 실제로 어떻게 대처해야 할지는, 해당 모듈을 찾는 것보다 더 어려운 문제입니다. 코드를 벤더링한다는 것은 해당 팀의 책임 하에 CVE를 관리해야 함을 의미하지만, 이러한 방식을 선택하는 대부분의 조직은 이에 필요한 인력을 확보하지 못하고 있습니다. 업스트림 유지보수자가 손을 뗀 코드에 수정 사항을 백포트하는 작업은 새로운 기능 개발보다는 회귀 테스트와 동작 호환성에 중점을 둔 전문적인 분야로, 이 때문에 대개 제품 팀의 업무 범위 밖으로 밀려나게 됩니다.
Go 빌드에서 변경해야 할 사항
Go에는 npm 스타일의 라이프사이클 훅이나 postinstall에 해당하는 기능이 없기 때문에, 의존성을 다운로드한다고 해서 공격자에게 무상으로 실행 지점을 제공하는 일은 없습니다. 이는 npm 및 PyPI에 비해 확실한 장점입니다.
하지만 이 장점에도 한계가 있습니다. Go 모듈 내의 코드는 여전히, 해당 모듈을 임포트하는 것이 빌드되거나 테스트되거나 실행될 때마다 결국에는 실행됩니다. 일반적으로는 시작 시 init() 함수가 호출되면서 실행되죠. 설치 시점에 자동으로 트리거되던 기능은 사라집니다. 하지만 코드 실행 경로는 여전히 존재하며, 이는 문제가 더 작아진 것이 아니라 전혀 다른 문제입니다.
어디서부터 시작해야 할지는 현재 진행 상황에 따라 달라집니다. 모듈 경로를 한 번도 검토한 적이 없다면, 이번 주에 모든 저장소에 대해 404 오류 검사를 실행하세요. 한 시간만 투자해도 작업에 활용할 수 있는 구체적인 목록을 얻을 수 있기 때문입니다. 의존성에 개인 GitHub 계정에 있는 모듈이 포함되어 있다면, `go mod vendor` 명령어를 사용하여 이를 벤더화하고 결과를 커밋하세요. 이렇게 하면 검토된 의존성 내용이 본인의 저장소에 포함되므로, 벤더 디렉터리를 사용하는 빌드 시 소유권이 변경될 수 있는 경로에서 해당 버전을 가져오지 않게 됩니다.
사용자 지정 도메인의 모듈을 해결하는 경우, 각 도메인의 등록 상태를 확인하십시오. 만료된 바니티 도메인은 단일 사용자 이름보다 더 광범위한 인수를 의미하며, 보호 기준이 전혀 없기 때문입니다. GOPRIVATE 또는 GONOSUMDB를 사용하는 경우, 패턴을 신중하게 검토하십시오. GONOSUMDB는 일치하는 모듈을 체크섬 데이터베이스 검증에서 제외하며, GOPRIVATE는 이를 재정의하지 않는 한 해당 제외의 기본값으로 작동합니다. 따라서 의도보다 광범위한 와일드카드를 사용하면, 제외하려던 것보다 더 많은 모듈에서 체크섬 데이터베이스 검증이 은밀하게 제거될 수 있습니다. 또한 조직 내에서 Go가 npm, PyPI, Maven 또는 crates.io와 함께 사용된다면, 이 모든 플랫폼에 대해 일관된 정책을 한 번에 수립해야 합니다. 공격자들은 여러 생태계를 넘나들며 활동하기 때문에, 한 곳만 철저히 보안 조치를 취한다고 해도 문제는 다른 곳으로 옮겨갈 뿐이기 때문입니다.
이 모든 것은 일회성 해결책이 아닙니다. 현재 유지보수자가 여전히 활동 중이라 안전하다고 여겨지는 모듈이라도, 앞으로도 계속 안전할 것이라는 보장은 없습니다.
이러한 위험 노출 정도는 조직 계정과 개인 계정 중 어느 쪽에 더 많은 코드가 속해 있는지에 따라 크게 달라집니다. 주로 golang.org/x, 주요 클라우드 SDK, 그리고 CNCF 프로젝트에 의존하는 Go 서비스는 일반적으로 이러한 특정 문제가 덜 발생합니다. 2019년에 단일 유지보수자가 관리하는 유틸리티 라이브러리 6개를 도입한 서비스의 경우 해결해야 할 과제가 산적해 있으며, 감사를 수행해야만 어떤 종속성에 주의를 기울여야 하는지 파악할 수 있습니다.
Go용 SecureChain의 활용 분야
위의 검증 절차만으로는 인수 시도를 막을 수는 없습니다. 이 절차들은 취약점이 있는 부분을 알려주므로 유용하지만, 예방과는 별개의 문제입니다. 남아 있는 과제는 모든 사용자가 동일한 바이트를 수신했는지 확인하는 것뿐만 아니라, 인증된 새 모듈 버전이 과연 신뢰할 만한지 여부를 판단하는 것이며, 바로 이것이 SecureChain for Go 가 바로 이를 해결하기 위해 설계된 것입니다. 이 도구는 소스 코드부터 모듈을 재컴파일하고, 모듈을 가져올 때 이를 검사하며, 모듈의 업스트림 개발이 중단된 후에도 알려진 취약점에 대한 패치를 지속적으로 적용합니다. 자세한 내용은 제품 페이지에서 확인할 수 있으며, TuxCare의 뉴스레터 을 통해 Go 관련 공급망 연구 결과가 발표될 때마다 최신 동향을 확인하실 수 있습니다.
자주 묻는 질문
리포재킹이란 무엇인가요?
프로젝트가 여전히 의존하고 있는 소스 저장소 경로를 되찾는 것으로, 대개 원래 계정이 삭제되거나 이름이 변경된 후에 발생합니다. 해당 경로는 여전히 의존성 파일에서 참조되고 있기 때문에, 되찾은 위치에 게시된 코드는 원래 경로를 신뢰했던 빌드로 해석됩니다.
왜 Go는 특히 리포지잭킹에 취약한가요?
Go는 소유권 기록이 있는 등록된 이름이 아니라 VCS 경로를 통해 모듈을 식별합니다. github.com/user/lib을 보유한 레지스트리 계정이 없으므로, 해당 계정 이름이 사용 가능해지면 이를 선점한 사람이 모듈 경로를 제어하게 됩니다.
Go 체크섬 데이터베이스가 리포지토리 탈취를 방지하나요?
그 자체만으로는 아닙니다. 이는 특정 모듈 버전을 다운로드하는 모든 사용자가, 첫 다운로드 시에도 공개 로그에서 해시를 조회할 때를 포함해, 동일한 콘텐츠를 받게 됨을 보장합니다. 그러나 해킹당한 경로를 통해 게시된 완전히 새로운 버전이 신뢰할 수 있는지 여부는 확인할 수 없습니다. 왜냐하면 해당 경로를 통해 게시된 진정한 새 버전이, 이후 하류에 있는 모든 사용자가 인증 기준으로 삼게 될 기록된 콘텐츠가 될 수 있기 때문입니다.
GitHub는 리포지토리 네임스페이스 탈취를 방지하나요?
부분적으로, 주로 인기 있는 저장소의 경우에만 해당됩니다. 네임스페이스 폐기 기능은 저장소가 지난 주 동안 100건 이상의 클론이나 100건 이상의 GitHub Actions 사용 기록을 기록했거나, 마켓플레이스에 등록된 액션이 포함된 경우 해당 소유자와 저장소 이름의 조합을 잠급니다. 규모가 작은 프로젝트는 이 기준에 미치지 못하므로, 해당 네임스페이스는 계속 확보할 수 있습니다. Go의 경우, 모듈 미러가 클론을 생성하지 않고도 수천 건의 다운로드를 처리할 수 있기 때문에 클론 기준은 더욱 완화되어 있습니다.
회수 가능성이 있는 Go 의존성을 어떻게 찾을 수 있나요?
`go list -m -f ‘{{.Path}}’ all` 명령어를 사용하여 모듈 경로를 모두 나열한 다음, 각 저장소 URL이 여전히 정상적인 응답을 반환하는지 확인하십시오. 빌드에 의존하는 경로에서 404 오류가 발생하면 추가 조사가 필요합니다. 이는 대개 업스트림 저장소가 사라졌음을 의미하지만, 비공개로 전환되었을 가능성도 있기 때문입니다. 어느 쪽이든, 해당 경로가 안전하다고 단정하기 전에 반드시 확인해야 하며, 한 번만 확인하는 것보다 정기적으로 점검하는 것이 좋습니다.
벤도링이 리포재킹을 방지해 주나요?
네, 벤더 디렉터리에 이미 커밋된 종속성의 경우 그렇습니다. 벤더링은 종속성 내용을 저장소로 복사하며, Go의 벤더 모드는 빌드 시 네트워크에 접속하지 않습니다. 다만 새로운 종속성이나 버전 업데이트의 경우 보호되지 않으며, 이 경우 여전히 평소와 같이 경로를 통해 해결됩니다.
Go 모듈은 설치 중에 코드를 실행하나요?
Go 모듈에는 npm의 설치 시 후크와 유사한 종속성 라이프사이클 스크립트가 없습니다. Go 모듈 내의 악성 코드는 해당 모듈을 임포트하는 테스트, 빌드 시 도구, 또는 프로덕션 바이너리 등 무엇이 실행되든 간에, 대개 시작 시 호출되는 init() 함수를 통해 실행됩니다.



