공급망 공격과 결정론적 빌드

·7분 읽기·작성자: SSP Editorial Team
지갑, 열쇠, 방패, 칩 아이콘과 '공급망 공격과 결정론적 빌드' 제목이 나란히 놓인 커버 일러스트

암호화폐 사용자를 위한 보안 조언의 대부분은 눈에 보이는 표면에 초점을 맞춥니다. 시드 문구, 클릭하는 링크, 로그인하는 사이트 말입니다. 이것들은 중요합니다. 그러나 그 모든 것 아래에는 더 조용한 위험이 자리합니다 — 소프트웨어 자체와, 그것이 무엇으로 만들어졌는가입니다. 지갑은 완벽하게 작성되어도 여전히 백도어를 함께 배포할 수 있습니다. 현대 애플리케이션은 수백 개의 서드파티 구성 요소와, 소스 코드를 당신이 설치하는 바이너리로 바꾸는 빌드 파이프라인으로 조립되기 때문입니다. 그 사슬의 어느 고리든 침해하면, 하류의 모든 사용자를 침해하게 됩니다.

이것이 소프트웨어 공급망이며, 공격자들은 그것이 암호화폐에서 가장 지렛대가 큰 표적 중 하나임을 알게 되었습니다. 이 글은 공급망 공격이 실제로 무엇인지, 왜 지갑이 그토록 매력적인 표적인지, 실전에서 버티는 방어책, 그리고 결정론적 빌드가 무엇을 가져다주는지를 — SSP가 이 모든 것을 어떻게 적용하는지 포함하여 — 설명합니다.

공급망 공격이란

공급망 공격은 애플리케이션에 직접 침입하지 않습니다. 대신 애플리케이션이 신뢰하는 무언가를 침해합니다. 끌어오는 의존성, 메인테이너의 계정, 또는 최종 산출물을 조립하는 빌드 파이프라인입니다. 악성 코드는 정상적인 업데이트에 실려 들어오며, 서명되고 일반 경로로 전달되기에, 당신이 설치하려던 바로 그 소프트웨어처럼 보입니다.

그 간접성이 바로 핵심입니다. 널리 쓰이는 라이브러리 하나 — 또는 빌드 서버 하나 — 를 공격하는 것만으로 하류의 수천 개 프로젝트와 수백만 명의 사용자에게 한꺼번에 도달할 수 있습니다. 암호화폐 지갑에게 그 보상은 직접적입니다. 지갑 안에서 실행되는 코드는 이미 가장 중요한 순간, 즉 거래가 서명되고 주소가 표시되는 순간에 접근할 수 있습니다. 변조된 의존성 하나는 피싱이나 허술한 시드 문구 위생으로 당신을 건드리지 않고도 목적지 주소를 바꿔치기하거나 키 자료를 빼낼 수 있습니다. 그래서 이 부류의 공격은 당신의 사고 모델에서 한 자리를 차지할 만합니다.

가까이서 와닿는 두 사례

두 건의 실제 사건이 이것이 어떻게 펼쳐지는지 보여 줍니다 — 하나는 암호화폐를 정면으로 겨냥했고, 다른 하나는 인터넷 전체를 거의 강타할 뻔했습니다.

event-stream과 Copay 지갑

2018년, event-stream이라는 인기 npm 패키지가 돕겠다고 나선 새 자원봉사 메인테이너에게 넘겨졌습니다. 오픈소스에서 끊임없이 일어나는 종류의, 일상적이고 선의로 보이는 인수인계였습니다. 그 후 새 메인테이너는 난독화된 악성 코드를 담은 새 의존성 flatmap-stream을 추가했습니다.

페이로드는 유난히 표적이 분명했습니다. 모두에게 발동하는 대신, 특정 하류 프로젝트 안에서만 활성화되었습니다. 바로 Copay 비트코인 지갑입니다. 그곳에서 그것은 그 애플리케이션 사용자의 시드 자료와 자금을 훔치도록 만들어졌습니다. event-stream을 끌어온 개발자 대다수는 결코 영향을 받지 않았습니다 — 코드는 자신이 찾는 피해자가 정확히 누구인지 알고 있었습니다.

이것은 "작은 라이브러리 하나만 설치했을 뿐"이 결코 이야기의 전부가 아니라는, 교과서적인 일깨움입니다. 당신은 그 라이브러리가 신뢰하는 모든 것도 함께 설치한 것입니다.

XZ Utils 백도어

2024년 XZ Utils 사건(CVE-2024-3094)은 더욱 끈기 있었습니다. XZ Utils는 대부분의 Linux 시스템에 조용히 존재하는 압축 라이브러리입니다. 수년에 걸쳐 한 공격자는 도움이 되는 기여자로서 신뢰를 쌓고, 차츰 메인테이너 권한을 얻은 뒤, OpenSSH — 전 세계 서버로의 원격 로그인을 지키는 소프트웨어 — 를 방해하도록 설계된 백도어를 몰래 심었습니다.

그것은 거의 우연히, 찰나의 지연을 알아챈 한 엔지니어 덕분에 잡혔습니다. 널리 배포되었다면 수많은 머신의 원격 접근을 넘겨줄 수 있었습니다.

암호화폐에 주는 교훈은 정신이 번쩍 들게 합니다. 그 공격은 영리한 버그를 노린 것이 아니라, 오픈소스의 신뢰 모델 그 자체를 노렸습니다. 수년에 걸친 사회공학 게임을 벌여 모두가 의지하는 사람이 되었던 것입니다.

실제로 통하는 방어

어떤 단일 통제도 공급망 공격을 막지 못합니다. 통하는 것은 그것들을 쌓아 올린 층이며, 각 층이 공격자의 선택지를 좁힙니다.

  • 고정된 의존성과 잠금 파일. 정확한 버전을 고정하고 잠금 파일을 버전 관리에 두면, 빌드가 더 새롭고 변조된 릴리스를 조용히 끌어올 수 없습니다. 업데이트는 자동이 아니라 의도적이고 검토 가능한 사건이 됩니다.
  • 최소한의 의존성. 추가하는 모든 패키지는 당신이 신뢰하는 한쪽 당사자입니다. 의존성이 적을수록 공격 표면이 작아지고, 침해될 수 있는 메인테이너도 줄어듭니다.
  • 의존성 샌드박싱. LavaMoat 같은 도구는 각 패키지가 런타임에 무엇을 할 수 있는지 제한하여, 침해된 의존성이 네트워크나 민감한 API에 자유롭게 닿지 못하게 합니다.
  • 코드 서명. 서명된 릴리스는 바이너리가 진짜 게시자로부터 왔으며 전송 중 변경되지 않았음을 사용자가 검증하게 해 줍니다.
  • 제3자 감사. 독립 보안 업체가 적대자의 눈으로 코드와 의존성을 검토하여, 내부 팀이 당연시하는 것을 잡아냅니다.
  • 재현 가능한 결정론적 빌드. 가장 강력한 구조적 방어이며, 자세히 이해할 가치가 있는 것입니다.

결정론적 빌드 설명

보통 같은 소스에서 두 번 빌드하면 약간 다른 두 바이너리가 나올 수 있습니다 — 타임스탬프, 파일 정렬 순서, 빌드 머신의 세부 정보가 새어 듭니다. 그 변동성은 문제입니다. 무해한 차이를 악의적인 차이와 구분할 수 없다는 뜻이기 때문입니다.

결정론적(또는 재현 가능한) 빌드는 그 변동성을 제거합니다. 같은 소스 코드라면 누구든, 어디서든, 어떤 머신에서든 바이트 단위로 동일한 산출물을 만들어 냅니다. 그 함의는 강력합니다. 독립적인 사람들이 공개 소스로부터 지갑을 다시 빌드하여, 당신이 내려받은 바이너리가 소스가 만들어 내는 것과 비트 단위로 일치함을 확인할 수 있습니다. 공격자가 빌드 파이프라인을 변조했거나 사후에 무언가를 끼워 넣었다면 해시가 일치하지 않으며, 변조는 즉시 드러납니다.

이는 신뢰 모델을 뒤집습니다. 더 이상 게시자의 말을 곧이곧대로 믿을 필요가 없습니다. 검증은 커뮤니티 전체가 수행하고 교차 확인할 수 있는 일이 됩니다. Reproducible Builds 프로젝트는 이 관행을 생태계 전반에 걸쳐 문서화하며, SLSA 같은 프레임워크는 당신이 프로젝트에 요구할 수 있는 빌드 무결성 보증 수준을 정의합니다.

SSP는 이것을 어떻게 적용하는가

SSP는 빌드 파이프라인을 사후의 고려가 아니라 위협 모델의 일부로 다룹니다. 지갑은 Dockerfile 우선의 결정론적 빌드로 배포됩니다. Docker로 정의된 동일한 환경이 매번 같은 산출물을 만들어 내므로, 게시된 릴리스를 공개 소스로부터 다시 빌드하여 당신이 내려받은 것과 비트 단위로 대조할 수 있습니다. SSP는 또한 Halborn의 독립 보안 검토를 거쳤으며, 그 감사는 코드와 그것이 의존하는 의존성을 모두 살핍니다.

SSP가 만들어진 방식에 고유한, 여기서 중요한 층이 하나 더 있습니다. SSP는 2-of-2 멀티시그 지갑입니다. 모든 거래에는 별도 기기에 있는 두 번째 키, 즉 SSP Key의 독립적인 승인이 필요합니다. 그것이 무엇을 하고 무엇을 하지 않는지 정확히 짚어야 합니다. 결정론적 빌드와 감사는 변조된 빌드가 애초에 배포될 가능성을 낮춥니다 — 그것들은 최전선입니다. 그러나 최악의 경우에도 — 어떻게든 빠져나간 빌드라도 — 두 번째 키는 여전히 모든 거래에 서명해야 하는 별도의 승인 표면입니다.

그것은 마법의 방패가 아니며, 침해된 빌드를 용인할 수 있게 만들지도 않습니다. 단지 한 기기의 변조된 단일 구성 요소가 그것만으로는 당신의 자금을 움직이지 못한다는 뜻입니다. 심층 방어가 핵심입니다.

당신이 직접 확인할 수 있는 것

이 모든 것에서 이득을 보기 위해 보안 엔지니어가 될 필요는 없습니다. 몇 가지 습관이 큰 도움이 됩니다.

  • 공식 출처에서만 내려받기. 지갑은 공식 사이트나 스토어 등록 페이지에서 받고, 메시지 속 링크나 검색 광고에서는 결코 받지 마세요. 이는 브라우저 확장 프로그램 위생에서 다루는 것과 같은 규율입니다.
  • 결정론적 빌드와 감사를 공개하는 프로젝트를 선호하기. 커뮤니티가 릴리스를 검증하게 하고 — 독립 검토에 비용을 치르는 — 프로젝트는 자신이 어떻게 사고하는지를 당신에게 말해 줍니다.
  • 서명과 해시가 제공되면 검증하기. 릴리스에 서명이나 체크섬이 딸려 있다면, 1분을 들여 대조하세요. 재현 가능한 빌드는 누군가 실제로 비교를 해야만 당신을 지켜 줍니다.
  • 전반적인 opsec을 단단히 유지하기. 공급망 방어는 더 큰 그림 안에 있습니다. 어느 한 층도 모든 무게를 짊어지지 않도록 opsec 체크리스트를 정기적으로 점검하세요.

이 가운데 무엇도 단일 당사자에 대한 신뢰를 요구하지 않습니다. 그것이 바로 자기 보관 소프트웨어에서 당신이 원하는 속성입니다.

계속 나아가기

지갑은 그것을 만든 사슬만큼만 신뢰할 수 있습니다. 좋은 소식은, 이것이 깔끔하고 구조적인 답을 가진 몇 안 되는 보안 문제 중 하나라는 점입니다. 결정론적 빌드에 독립 감사를 더하면 "우리를 믿으세요"가 "직접 검증하세요"로 바뀝니다.

피싱 인식, 시드 문구 모범 사례, 브라우저 확장 프로그램 위생, 그리고 정기적인 opsec 체크리스트로 방어를 계속 쌓아 가세요. 각각은 하나의 문을 닫고, 합쳐졌을 때 비로소 자기 보관 보안이 실제로 어떤 모습인지를 이룹니다.

이 글 공유하기

관련 글