메뉴
HN
Hacker News 48일 전

누구도 공급망 보안을 보장해주지 않는다

IMP
7/10
핵심 요약

Rust의 패키지 저장소인 crates.io를 겨냥한 공급망 공격 비판에 대해 패키지 생태계의 근본적인 한계를 지적하며 반박하는 글입니다. 네임스페이스 도입이나 샌드박싱 같은 기술적 해결책이 오히려 사용자의 인지적 부담을 가중시키거나 시스템적 한계에 부딪힌다고 설명합니다. 또한 저장소 코드와 일치 여부 검증 등 제기되는 문제들이 실질적으로 해결 불가능한 근본적인 딜레마를 내포하고 있음을 강조합니다.

번역된 본문

누구도 공급망 보안을 보장해주지 않는다

혹시 모르실 수 있으니 말씀드리자면, 저는 개발자가 아닙니다. 저는 컴퓨팅 파워의 비최적적 사용에 짜증을 내는 자폐 스펙트럼의 고양이 소녀일 뿐이며, 이 문제를 해결하다 보니 우연히 프로그래밍을 하게 되었습니다. 결정적으로, 이 과정에는 백스테이지에서 기반 기술에 대해 논의하는 것도 포함되며, 그 덕분에 저는 이 분야의 사회적 측면을 더 잘 인식하게 된 것 같습니다. 그래서 저는 공급망 공격과 관련해 crates.io를 비판하는 것에 대해 제 생각이 있습니다. 비슷한 글이 한두 편이 아니고 나온 지금, 왜 그런 비판이 핵심을 벗어났는지 몇 마디 하고자 합니다.

오타 스쿼팅(Typo-squatting) 핵심을 다루기 전에, 애초에 공급망 공격이 어떻게 발생하는지, 그리고 이를 고치려는 일반적인 아이디어가 왜 효과가 없는지 이야기해 봅시다. 악의적인 의존성이 프로젝트에 추가되는 데에는 여러 이유가 있습니다. 가장 노골적인 이유 중 하나가 오타 스쿼팅입니다. 이는 악의적인 라이브러리가 진짜 라이브러리와 비슷한 이름을 가질 때 발생합니다(예: num_cpu vs num_cpus). 흔히 제시되는 해결책으로는 직접 URL을 사용하거나 네임스페이싱(Namespacing)을 도입하는 것이 있습니다. 자, 이게 도움이 되는지 봅시다. Cargo.toml에 다음 줄들을 추가하는 Pull Request(PR)를 받았다고 상상해 보세요:

[dependencies] bitflags = { git = "https://github.com/bitflags/bitflags" } itertools = { git = "https://github.com/itertools/itertools" } rand_core = { git = "https://github.com/rust-random/rand_core" }

이 URL 중 하나는 가짜입니다. 어느 것인지 알 수 있나요? 정답은 itertools입니다. 올바른 URL은 https://github.com/rust-itertools/itertools 입니다. https://github.com/itertools 는 어떤 랜덤한 계정입니다. 참고로 https://github.com/rust-bitflags 는 아예 등록조차 되어 있지 않습니다. 사용하는 모든 패키지의 URL을 기억할 수 있다고 생각한다면, 아마 틀릴 것입니다. 많은 크레이트(crates)가 개인이 아닌 깃허브 조직에서 관리되기 때문에 dtolnay나 BurntSushi를 (아마도) 신뢰할 수 있다는 것만 기억하는 것으로는 충분하지 않습니다. 이것마저도 보수적인 접근이 아닙니다. https://gitlab.com/BurntSushi 는 사용 가능하고 https://glthub.com 는 판매 중이므로, 공격자에게는 선택할 수 있는 다른 방법이 무궁무진합니다. crates.io 내부의 네임스페이싱, 깃허브 조직, 도메인 등 어떤 방식을 통해 크레이트 ID를 더 길게 만들면, 사용자가 이를 정확히 기억하기가 더 어려워지고, 결과적으로 오타 스쿼팅을 인식하기도 더 어려워집니다.

샌드박싱(Sandboxing) Rust는 빌드 스크립트(build.rs)와 절차적 매크로(procedural macros)에 PC에 대한 전체 접근 권한을 부여합니다. 더 심각한 것은, 프로젝트 디렉터리를 열면 rust-analyzer가 cargo check를 실행하므로, 이는 사실상 0-클릭 원격 코드 실행(0-click RCE)이 될 수 있습니다. 몇몇 사람들이 이 문제를 해결하려고 시도했습니다. build.rs 샌드박싱에 대한 오픈 이슈가 있고, 절차적 매크로를 WebAssembly로 컴파일하는 실험도 있었습니다. 하지만 이는 거의 실용적이지 않습니다. cargo build는 안전해질 수 있지만, 보통 그 직후에 샌드박스화가 불가능한 cargo test나 cargo run을 실행하게 됩니다. Rust 개발을 안전하게 만드는 것은 빌드 시간 이상을 포함하며, 카고(cargo) 단독으로 책임질 수 없는 강력한 시스템 수준의 격리가 필요합니다.

VCS의 코드 자주 제기되는 또 다른 문제는 crates.io에 있는 코드와 Git 저장소의 코드가 항상 일치하지 않는다는 것입니다. 기본적으로 이는 해결하기가 쉽지 않습니다. crates.io를 크레이트 이름을 저장소 URL에 매핑하는 DNS처럼 만들 수는 없습니다. crates.io는 항목을 삭제함으로써 다운스트림 사용자에게 피해를 줄 수 있는 권한을 패키지 관리자에게 주지 않도록 설계되었기 때문입니다:

crates.io의 주요 목표 중 하나는 시간이 지나도 변경되지 않는 크레이트의 영구적인 아카이브 역할을 하는 것이며, 버전 삭제를 허용하는 것은 이 목표에 위배됩니다.

이 제한은 아마도 유명한 라이브러리가 npm에서 삭제되어 CI 빌드가 중단되었던 left-pad 사건 때문에 설정된 것으로 보입니다. npm은 중앙 집중식이기 때문에 이 문제를 빠르게 해결할 수 있었습니다. 단순한 역할만 하는 thin crates.io는 이를 감당할 수 없었을 것이므로, 사본을 저장하고 제공하는 방식을 택한 것입니다.

crates.io는 cargo publish 시점에 저장소에서 파일을 가져올 수는 있을 것입니다. 하지만 관리자가 그 후에 강제 푸시(force-push)를 해버릴 수도 있다면, 이는 좋은 보안 메커니즘이 아닙니다. 어쩌면 crates.io가 주기적으로 저장소의 기록 변경을 스캔할 수도 있을 것입니다. 하지만 그것이 정확히 무엇을 의미하는 걸까요?

원문 보기
원문 보기 (영어)
No one owes you supply-chain security | purplesyringa's blog No one owes you supply-chain security April 11, 2026 Lobsters In case you’re unaware, I’m not a developer. I’m actually an autistic catgirl annoyed by suboptimal use of computing power, and fixing that happens to involve programming. Crucially, it also includes discussing foundational technology with people behind the scenes, and apparently that makes me more aware of social aspects of this sphere. So, I have opinions about criticism of crates.io for supply-chain attacks . After a dozen similar articles, I have some select words to voice about why it’s off the mark. Typo-squatting Before I cover the main point, let’s talk about about how supply-chain attacks happen in the first place, and why some common ideas for fixing them don’t work out. There are multiple reasons when a malicious dependency is added to a project. The least discreet reason this can happen is typo-squatting. It happens when a malicious library has a name similar to a real library, e.g. num_cpu vs num_cpus . Commonly cited solutions include using direct URLs or namespacing. Well, let’s see if that helps. Say you get a PR adding the following lines to Cargo.toml : [dependencies] bitflags = { git = "https://github.com/bitflags/bitflags" } itertools = { git = "https://github.com/itertools/itertools" } rand_core = { git = "https://github.com/rust-random/rand_core" } One of these URLs is fake. Can you tell which one? It’s itertools – the correct URL is https://github.com/rust-itertools/itertools . https://github.com/itertools is a random account. https://github.com/rust-bitflags is not registered at all, by the way. If you think you can remember the URLs for each package you use, you’re probably wrong. Since many crates are managed by GitHub organizations, not individuals, it isn’t even enough to remember that you can (likely) trust dtolnay and BurntSushi . Though this still isn’t conservative enough – https://gitlab.com/BurntSushi is free and and https://glthub.com is on sale, so attackers have plenty other choices. By making crate IDs longer, whether by namespacing within crates.io , GitHub organizations, or via domains, you only make it harder for users to remember them precisely, and thus harder to recognize typo-squatting. Sandboxing Rust gives build scripts and procedural macros full access to your PC. Worse, rust-analyzer runs cargo check when you open the project directory, so it can effectively become a 0-click RCE. Some people tried to solve this. There’s an open issue for build.rs sandboxing, and there were some experiments about compiling procedural macros to WebAssembly. But this is hardly workable. While cargo build can become safe, you usually run cargo test or cargo run immediately afterwards, which is impossible to sandbox. Making Rust development secure involves more than build time and requires powerful system-level isolation that cargo alone cannot be responsible for. Code in VCS An oft brought-up issue is that the code on crates.io and in Git don’t always match. To begin with, this is not trivial to solve. You can’t just turn crates.io into a DNS, mapping crate names to repository URLs, since crates.io is designed to avoid giving crate maintainers the ability to break downstream consumers by deleting stuff: One of the major goals of crates.io is to act as a permanent archive of crates that does not change over time, and allowing deletion of a version would go against this goal. This restriction was likely set due to the left-pad incident , when a popular library was deleted from npm , breaking CI builds. npm could quickly fix this because it’s centralized. Thin crates.io wouldn’t stand a chance, so it saves and serves copies. crates.io could still pull files from the repo on cargo publish . But if the maintainer can just force-push afterwards, it’s not a good security mechanism. Maybe crates.io could periodically scan repositories for history changes. But what does that mean exactly? Does removing the release commit from master , but keeping it on a tag count? What if I host the repo on a custom forge, which serves one history to the crates.io User-Agent and different history to the rest of us? Or maybe there’s a good reason to have different code in Git and crates.io . If the crate contains autogenerated code, you should probably generate it in CI on release. Wouldn’t want to run expensive codegen in build.rs on each install, would you? Every option has downsides: they can break existing packages or have false-positives on benevolent rewrites . I’d still like cargo audit to scan repositories, but it can’t be a hard limit, and that means it can be designed around. Moderation All these issues have an unacknowledged shared assumption that keeping malicious code off crates.io is “Rust’s” responsibility. That if you decide to use a dependency and then cargo add totally-safe-package steals your credentials, it’s an inherent fault of crates.io . Which is really misplaced if you think about how Rust is developed. I’m sure many of you use open-source software and remember the MIT license: THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, […], FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. […] This applies just as much to Rust. Despite its popularity, Rust is not sponsored by large companies, like Microsoft or RedHat, in the same sense that GitHub or Linux are. The compiler and std are primarily developed by volunteers, who don’t get anything out of it except for rare donations from other members of the community. The fact that this scheme works at all is ridiculous to me. Perhaps it’s so unbelievable, that people think there’s secret employment in Rust Foundation or something, when in fact it only paid four software engineers in 2024 , excluding grants. So it feels bonkers to me when people say they know a solution to supply-chain attacks, such as (underline the correct variant): Making Linux distro maintainers responsible instead (duplicating work). Sandboxing as much as possible (which is to say, not a lot and not by much). Moving stuff into the standard library (overloading T-libs more than it already is). Your other favorite technical solution. Futilely fixing societal problems with technology is every developer’s forte, but let’s just be real for a second. The issue is the lack of manpower, not that no one made a nice GUI for blockchain-powered Web of Trust yet, or whatever. We’re not nearly close to the level of security a centralized registry can provide. On the software side, in 2025 Rust teams made or piloted tools for typo squatting detection, dynamic build script analysis, and real-time code scanning. On the personal side, Rust Foundation hired on-call engineers in 2025 and a second infrastructure engineer in 2026. If that sounds overdue, well, they had net loss in 2023 – software isn’t cheap. You can’t expect the same level of security from a small, pre-moderated, stable-release, private-funded registry, like Ubuntu’s, and a large, post-moderated, rolling-release, mostly-voluntary registry. The scale doesn’t compare, and suggesting that complicating an overworked team’s job will resolve the issue is just insulting. Audit What I’m saying is that you’re responsible for auditing the crates you use. Maybe that’s not how it’d work in a perfect world, but in our capitalist hellscape no one else can be argued to be accountable for this, which leaves you, the crate user. And Rust gives you all the tools for this. Lockfiles limit installation to verified versions. cargo add &LTdep>@&LTversion> installs the crate at a specific version. cargo-vet can be useful. crates.io shows a 90-day download plot, which is difficult for malicious crates to simulate, and is responsive to malware reports via email. If you’re feeling adventurous, you can check the submitted crate sources by clicking “browse source” in the crate’s sidebar. A quick check covers most simple attacks. cargo update --dry-run show