Principais conclusões
- A substituição da fonte de carga pressupõe que a substituição atenda ao mesmo conteúdo da caixa; é por isso que não é necessário alterar o arquivo do projeto.
- A seleção feita pelo registro avalia uma caixa com base no que ela contém, e não na reputação do seu nome.
- O caminho de verificação é um
.cargo/config.tomlalteração, sem edições emCargo.tomlouCargo.lock. - As caixas modificadas são a exceção, e optar por uma delas é uma mudança deliberada e evidente.
Confiar no crates.io por padrão raramente é uma decisão que alguém toma de propósito. É simplesmente o padrão — até o momento em que alguém analisa a questão diretamente. A próxima pergunta geralmente é alguma variação do que fazer em vez disso, e é aí que um registro privado do Cargo deixa de ser uma opção óbvia.
O termo abrange pelo menos três configurações diferentes, e o Cargo as trata de maneiras tão distintas que escolher a opção errada pode causar problemas mais adiante. Uma delas não requer alterações nos arquivos do projeto. Outra implica alterar cada manifesto que declara as dependências que você deseja redirecionar. A maioria das equipes só descobre qual é qual quando já está no meio do processo de tentativa.
Vale a pena destacar uma exceção antes de mais nada, pois é algo que só se descobre mais tarde. A verificação não envolve alterações no arquivo do projeto. Os crates com patches, por outro lado, sim, e optar por um deles é sempre uma edição deliberada.
O que geralmente significa um Registro Privado de Cargas
Três abordagens são discutidas sob um mesmo título: vendoring, substituição de fonte (espelhamento) e registros alternativos, incluindo os privados. A própria documentação do Cargo trata a substituição de fonte como algo distinto de um registro privado, e não como um tipo de registro privado.
| Arquitetura | O conteúdo pode diferir do site crates.io | Requer alterações no arquivo Cargo.toml |
|---|---|---|
| Inserção de arquivos no repositório | Sim, é seu | Não, mas acrescenta um vendor/ diretório |
| Espelho de reposição original | Não, deve ser a mesma coisa | Não |
| Registro alternativo | Sim | Sim, por dependência |
A substituição de fonte é aquela que gera a afirmação de que não há alteração no fluxo de trabalho, e a razão para isso é que o Cargo presume que o conteúdo do crate seja o mesmo em ambos os lados.
A substituição da fonte de carga foi projetada para espelhos. Ela pressupõe que a substituição contenha a mesma fonte que a original e não permite que a substituição introduza caixas que a original não possua. Vale a pena ser preciso aqui, pois é nesse ponto que muitos textos de vendedores exageram.
O Cargo não volta atrás para verificar se a substituição está correta no crates.io. O que realmente é feito em um projeto real é Cargo.lock, já que uma soma de verificação já registrada ali não corresponderá a um crate cujo conteúdo tenha sido alterado, e a compilação falhará em vez de simplesmente aceitar o substituto sem avisar.
Isso parece uma limitação. Para um espelho de verificação, esse é justamente o objetivo — espera-se que o substituto forneça à sua compilação o mesmo conteúdo do crate que já havia sido resolvido.
O SecureChain funciona da mesma maneira, em essência. Ele recompila crates a partir do código-fonte, verifica-os e se integra à sua configuração existente, de modo que os desenvolvedores continuem executando os mesmos comandos do Cargo de sempre.
Duas maneiras pelas quais a Cargo permite que você redirecione as caixas
A substituição da fonte está localizada em .cargo/config.toml e não altera nada no seu projeto.
[source.crates-io]
replace-with = "securechain"
[source.securechain]
registry = "<your SecureChain registry index URL>"
Um registro alternativo é o outro mecanismo, e ele funciona de maneira diferente por definição.
[registries.securechain]
index = "<your SecureChain registry index URL>"
Com um registro alternativo, cada dependência precisa especificar seu nome.
[dependencies]
serde = { version = "1.0", registry = "securechain" }
Essa é uma alteração feita por dependência em todos os manifestos que declaram uma dessas crates, o que representa exatamente o custo de migração que a estratégia de substituição da fonte evita.
Análise da realidade: Um fornecedor que afirme tanto que oferece crates modificados quanto que nada nos arquivos do seu projeto é alterado está descrevendo algo que a substituição padrão de código-fonte do Cargo não suporta. Pergunte qual dos dois mecanismos uma integração realmente utiliza, e o restante da avaliação se torna muito mais simples.
Por que a substituição do código-fonte mantém seu arquivo de bloqueio válido
A primeira pergunta que um engenheiro de Rust meticuloso faz é se Cargo.lock falhas. No caminho de verificação, isso não ocorre, e o motivo é importante, pois também é o argumento mais sólido disponível.
A .crate O arquivo é um arquivo tar compactado contendo o código-fonte do crate empacotado. A verificação de checksum do próprio Cargo confirma que o arquivo baixado corresponde ao checksum esperado com base nos metadados do registro e no arquivo de bloqueio, e isso é tudo o que ele faz. Ele não recompila nada. A etapa independente de recompilação e comparação ocorre a montante, em um serviço como o SecureChain, antes que um crate seja fornecido por meio da fonte substituta.
Quando uma recompilação diverge do artefato publicado, essa discrepância passa a ser um indício a ser investigado, em vez de algo que seja simplesmente ignorado. A divergência, por si só, não é prova de adulteração, já que peculiaridades da compilação e etapas de lançamento não reproduzíveis também podem causá-la. O importante é que ela vem à tona, em vez de desaparecer.
Portanto, a propriedade útil não é o fato de a verificação ser invisível. É o fato de que os crates verificados chegam inalterados, enquanto qualquer coisa inesperada se torna visível, em vez de ser aceita silenciosamente.
rm -rf ~/.cargo/registry
cargo build --locked
Se o cache do Registro estiver limpo e, além disso, --locked gera uma compilação bem-sucedida com base no seu código inalterado Cargo.lock, a alegação de compatibilidade é comprovada no seu ambiente, e não em um slide. --locked é o sinalizador que importa aqui, pois ele gera uma falha em vez de reescrever silenciosamente o arquivo de bloqueio. Limpar o cache primeiro é o que faz com que todos os crates sejam baixados e tenham sua soma de verificação verificada novamente.
Dica profissional: Execute o projeto-piloto com --locked e --offline em etapas separadas. --locked isso comprova que o arquivo de bloqueio existente pode ser usado sem alterações. --offline confirma que a compilação é concluída sem acesso adicional à rede. Para confirmar que nada foi baixado diretamente do crates.io, use o registro de tráfego de saída ou uma regra de firewall (consulte a Fase 4), em vez de confiar em --offline sozinho.
O que o rastreamento detecta e o que não detecta
Um registro privado de cargas justifica sua existência diante de duas ameaças relacionadas, e nenhuma delas é resolvida simplesmente verificando um nome ou uma soma de verificação.
A primeira é a falsificação de identidade, em que uma crate é publicada com um nome tão parecido com um nome popular que um erro de digitação ou uma olhada rápida em um Cargo.toml, acaba instalando mesmo assim. A segunda é a “tomada de controle”, em que um projeto no qual se confia há anos muda de mãos — seja por causa da saída de um mantenedor, do comprometimento de uma conta ou de uma transferência discreta de propriedade — e passa a distribuir algo diferente sob um nome no qual sua equipe já confia.
Nenhuma das duas coisas depende de uma linha de código vulnerável localizada em algum ponto da árvore de dependências. Ambas dependem do modelo padrão do crates.io, onde qualquer pessoa pode publicar, e um nome com histórico não diz nada sobre quem o controla atualmente.
A verificação antes que um crate chegue à sua compilação tem exatamente esse objetivo. O crate é recompilado a partir do código-fonte e verificado quanto a padrões de malware conhecidos e comportamentos suspeitos, de modo que é avaliado com base em seu próprio conteúdo, em vez de herdar confiança apenas por causa de seu nome. Isso reduz consideravelmente as chances. Não é uma garantia de que todos os crates maliciosos serão identificados, e qualquer fornecedor que ofereça essa garantia está exagerando.
Além disso, essa garantia é diferente da garantia de que nada em sua compilação jamais executará código arbitrário. A verificação ocorre antes que o crate seja incluído. Uma vez que ele esteja em sua árvore de código, o seu próprio cargo build funciona como sempre funcionou.
| Camada | Quem faz a triagem ou o teste em ambiente isolado? | O que ele captura |
|---|---|---|
| Crie o diretório no registro, antes da instalação | O registro | Falsificação de identidade, invasão de contas, padrões de malware conhecidos |
O seu próprio cargo build |
O Cargo não o coloca em sandbox | build.rs e as macros proc continuam sendo executadas, a menos que você adicione isolamento externo |
Como as caixas remendadas alteram a integração
É também nesse ponto que a aquisição deixa de ser apenas uma questão de triagem. Uma caixa cuja propriedade muda discretamente de mãos não é um problema pontual de detecção. Trata-se de uma lacuna contínua na manutenção, e a triagem de uma única versão não a resolve.
A continuação da aplicação de patches em crates que foram abandonados pelo desenvolvedor original é um serviço distinto da verificação e resulta em crates cujo conteúdo difere deliberadamente do que foi publicado no crates.io. Conteúdos diferentes significam somas de verificação diferentes, e a substituição de código-fonte rejeita, por padrão, qualquer coisa que não corresponda.
Portanto, a aplicação de patches é uma opção explícita por crate. Para uma dependência declarada no seu próprio manifesto, basta indicar o nome do registro alternativo.
[dependencies]
abandoned-crate = { version = "1.2.4", registry = "securechain" }
Para uma caixa que só aparece de forma transitiva, essa declaração não tem efeito algum, pois ela rege apenas a dependência que você definiu. Uma declaração de nível superior [patch] A substituição é o mecanismo criado para o caso transitivo.
[patch.crates-io]
abandoned-crate = { version = "1.2.4", registry = "securechain" }
Entre Cargo.toml manifestos, Carga indica [patch] apenas a partir daquela localizada na raiz do espaço de trabalho e, a partir daí, ela se aplica a todo o gráfico de dependências. Ela também pode estar localizada em .cargo/config.toml, embora a documentação do Cargo desaconselhe isso para qualquer arquivo compartilhado, já que os arquivos de configuração geralmente não são incluídos no controle de versão.
A estrutura prática de uma implantação real é, portanto, de duas camadas.
| Necessidade | Mecanismo | Alterações no arquivo do projeto |
|---|---|---|
| Caixas verificadas, sem modificações | Substituição da fonte | Nenhum |
| Caixas abandonadas remendadas | Registro alternativo ou [patch] |
Uma pequena entrada explícita por caixa |
Vale a pena mencionar em voz alta algo que essa tabela não mostra. A escolha de um crate modificado pode alterar a fonte e a soma de verificação registradas em Cargo.lock, portanto, o arquivo de bloqueio é atualizado, e essa atualização deve ser confirmada e revisada como qualquer outra alteração de dependência. A verificação não altera o arquivo de bloqueio. A aplicação de patches, sim.
Essa é uma explicação melhor do que uma afirmação genérica, pois o comprador de serviços de segurança pode verificar quanto custam as correções antes de assinar qualquer contrato. Uma pequena alteração deliberada para cada contêiner que eles decidirem corrigir é uma solicitação razoável. Descobrir, no meio do projeto-piloto, que todos os manifestos precisam ser editados, não é.
Auditorias de bases de código do setor divulgadas pela SC Media revelaram que 93% das bases de código analisadas continham componentes sem atividade de desenvolvimento nos últimos dois anos. Para aqueles que foram efetivamente abandonados nesse grupo, não há mais nenhum mantenedor upstream disponível para lançar uma versão corrigida, e fazer o backport significa reconstruir o contexto do zero. Reproduza o bug, escreva uma correção que não altere o comportamento do qual alguém dependa e valide-a sem poder contar com a revisão ou o julgamento de um mantenedor ativo. A posição da TuxCare com o SecureChain nesse contexto é a de uma gestão de longo prazo, em vez da substituição do desenvolvimento original, e é por isso que os dois recursos se integram de maneira diferente.
Colocando em prática
Quatro fases, organizadas de forma que cada uma forneça evidências antes que a seguinte amplie o escopo.
| Fase | Ação | O que isso comprova |
|---|---|---|
| 1 | Substituir a fonte em um serviço, limpar o cache | As somas de verificação continuam válidas |
| 2 | cargo build --offline após a Fase 1 |
A compilação é concluída utilizando apenas o que já foi baixado na Fase 1 |
| 3 | Implementar o CI em todos os repositórios | Quais pipelines tinham uma configuração de origem codificada estaticamente |
| 4 | Bloquear o acesso externo ao índice público do crates.io e aos pontos de extremidade de download | O que estava sendo silenciosamente ignorado na configuração |
É na Fase 3 que essas implementações realmente dão errado. A Cargo se funde .cargo/config.toml arquivos do diretório atual e, em seguida, dos diretórios superiores, até que, em caso de falha, recorra a $CARGO_HOME/config.toml, sendo que o arquivo mais profundo prevalece e o diretório raiz ocupa a posição mais baixa. Uma imagem de CI ainda pode introduzir configurações que ninguém esperava, e o diretório a partir do qual o cargo foi executado determina quais arquivos serão incluídos na fusão. Verifique a configuração efetiva no executor, em vez de tentar deduzi-la.
cargo +nightly -Z unstable-options config get --show-origin source
Isso informa a configuração efetiva da fonte e a origem de cada valor. Execute-o em um agente de compilação, em vez de em um laptop, já que os dois costumam ser diferentes e apenas um deles está documentado.
Observação: Isso requer o Cargo noturno com -Z unstable-options, portanto, não é algo que deva ser incorporado a um pipeline de CI estável tal como está.
Dica profissional: execute a Fase 4 no ambiente de teste por uma semana antes de migrar para a produção. Bloqueie o índice esparso em index.crates.io e os downloads em static.crates.io; e, caso algum sistema da frota ainda utilize o protocolo Git, o repositório do índice do crates.io no github.com também precisará ser bloqueado. Os pipelines que apresentarem falhas geralmente revelarão caminhos que ninguém sabia que ainda estavam fazendo pull diretamente do crates.io, e essa lista tem mais valor como documento do que como análise pós-incidente.
Quando um registro privado por si só não é suficiente
O espelhamento verificado, ou seja, a substituição da fonte, resolve o problema de “será que este é o mesmo pacote?”. O crate que é incluído na sua compilação é aquele que seu arquivo de bloqueio já resolveu. Isso não indica se o crate era confiável desde o início, e é exatamente isso que um nome semelhante ou uma invasão de crate realmente exploram.
O SecureChain para Rust abrange ambos os aspectos. Cada crate é recompilado a partir do código-fonte e sua integridade é verificada; em seguida, é analisado em busca de comportamentos maliciosos e suspeitos antes de chegar até você. Assim, versões vítimas de typosquatting ou de invasão são avaliadas com base no que contêm, em vez de serem consideradas confiáveis apenas pelo nome e pelo histórico.
Além disso, ele continua aplicando correções ao que já está em sua pilha mesmo depois que os mantenedores deixam de dar notícias; e, quando um crate chega ao fim de sua vida útil, o Suporte de Ciclo de Vida Infinito fornece as correções retroativas de forma contínua, para que uma dependência abandonada não se torne, sem que ninguém perceba, aquela que ficou sem correções.
Veja quanto custa a cobertura verificada e corrigida para sua árvore de dependências real e solicite um orçamento.
Perguntas frequentes
O que é um registro privado de cargas?
Um registro privado de crates do Cargo é um registro alternativo, interno ou operado por um provedor, para crates do Rust. Ele difere da inclusão de crates por meio de um provedor no seu repositório e do espelhamento do crates.io por meio da substituição de fonte do Cargo. Essas opções controlam a origem dos crates de maneiras diferentes, mas apenas um registro alternativo funciona como um registro privado nos termos do próprio Cargo.
O espelhamento do crates.io por meio da substituição da fonte causa problemas no Cargo.lock?
Não quando a fonte de substituição fornece crates verificados e não modificados, pois o conteúdo é o mesmo e as somas de verificação registradas ainda são válidas em relação ao seu arquivo de bloqueio existente. Crates com conteúdo modificado não podem, de forma alguma, ser introduzidos por meio da substituição da fonte.
Como faço para configurar a substituição de código-fonte no Cargo?
Adicione um [source.crates-io] bloco com replace-with ao indicar uma fonte específica, defina essa fonte com a URL do índice do registro, tanto em .cargo/config.toml. Nenhuma alteração em Cargo.toml ou Cargo.lock são necessários.
A substituição da fonte detecta crates vítimas de typosquatting ou que foram sequestrados?
A substituição da fonte, por si só, não faz isso. Sua função é redirecionar o Cargo para uma fonte equivalente, enquanto o conteúdo do crate permanece o mesmo. Identificar um nome semelhante ou um crate que mudou de proprietário é uma função de triagem, avaliada quando um crate é reconstruído pela primeira vez e novamente à medida que novas versões são publicadas; não é uma propriedade do mecanismo de redirecionamento.
Como funcionam as caixas com patches no Cargo?
As caixas com patches têm conteúdo diferente do publicado pelo crates.io; portanto, não podem ser fornecidas por meio da substituição da fonte. Uma dependência direta pode apontar para um registro alternativo, enquanto uma dependência de nível superior [patch] A substituição sobrepõe a cópia corrigida em todo o gráfico de dependências, incluindo usos transitivos. De qualquer forma, o arquivo de bloqueio registra a alteração.
Como posso verificar por conta própria a alegação de compatibilidade?
Limpar ~/.cargo/registry, em seguida, execute cargo build --locked. Uma compilação bem-sucedida com o cache limpo, usando seu arquivo de bloqueio inalterado, demonstra que o código-fonte substituto é compatível com o conjunto de dependências bloqueadas e que os pacotes baixados atendem às somas de verificação esperadas pelo Cargo. Executando cargo build --offline Posteriormente, o sistema confirma que a compilação foi concluída sem necessidade de acesso adicional à rede; no entanto, ele não confirma a origem do conteúdo já armazenado em cache; portanto, utilize o registro de tráfego de saída ou uma regra de firewall para isso.



