Principais conclusões
- Um proxy do módulo Go foi desenvolvido para servir arquivos, e não para avaliar se esses arquivos são seguros para serem executados.
- Um caractere na sua string GOPROXY determina se um proxy inativo falha de forma evidente ou se envia as compilações discretamente para a internet pública.
- O GOPRIVATE ignora o banco de dados público de checksums para cada caminho com o qual ele faz correspondência; portanto, um padrão abrangente pode desativar essa proteção em uma escala muito maior do que o pretendido.
Em maio de 2026, pesquisadores identificaram uma onda de módulos Go maliciosos publicados em uma conta do GitHub chamada BufferZoneCorp. O roubo de credenciais fazia parte da carga maliciosa, mas o mais interessante era o alvo de vários desses módulos Go: a própria infraestrutura de compilação. Eles corromperam o GOPROXY, enfraqueceram a verificação de checksum, editaram o arquivo go.sum e inseriram wrappers Go falsos nos caminhos do fluxo de trabalho, de modo que as etapas posteriores de CI executassem o binário do invasor em vez da cadeia de ferramentas real. A equipe de segurança do Go bloqueou os módulos identificados. A técnica ainda funciona em qualquer lugar onde a infraestrutura de módulos seja tratada como configuração, em vez de como uma barreira de segurança.
O cache confere a essa infraestrutura uma memória de longo prazo. Um módulo pode permanecer acessível muito tempo depois que seu código-fonte tenha sido alterado ou desaparecido, e uma brecha de segurança anterior no Go mostrou até onde isso pode chegar — o proxy público de módulos do Go continuou servindo uma versão maliciosa por mais de três anos depois que a tag correspondente no GitHub havia sido discretamente reescrita (mais detalhes sobre isso a seguir). Esse problema de persistência é um dos principais motivos pelos quais as equipes implementam um proxy interno de módulos e configuram o GOPROXY para apontar para ele.
Esse instinto não está errado. A reprodutibilidade da compilação melhora, as dependências armazenadas em cache deixam de depender da acessibilidade do proxy público e, finalmente, há um único lugar para verificar de que a organização depende.
O que não se segue é a conclusão sobre segurança. Um proxy de módulo fornece bytes. O protocolo em si não possui nenhuma etapa de verificação de conteúdo nem mecanismo de política, e não faz nenhum julgamento sobre o que está contido nos arquivos que retorna. Executar seu próprio proxy significa controlar o cache — o que é útil, mas não é a mesma coisa que controlar o que entra nele.
O que um proxy do Go Module realmente faz
O protocolo de proxy do módulo é propositalmente simples. Um proxy é um servidor HTTP que expõe alguns pontos de extremidade por caminho de módulo:
| Ponto final | Devoluções |
| $module/@v/list | Versões conhecidas, uma por linha |
| módulo/@v/versão.info | Metadados JSON, incluindo o carimbo de data e hora da versão |
| módulo/@v/versão.mod | O arquivo go.mod dessa versão |
| módulo/@v/versão.zip | O arquivo com o código-fonte do módulo |
| $module/@latest | A versão que o proxy considera mais recente |
Isso é tudo. Nada nessa lista contém uma assinatura, um resultado de verificação, uma avaliação de política ou qualquer afirmação verificável sobre a origem do código.
Observação: Como o protocolo consiste em chamadas HTTP GET simples em um esquema de caminhos previsível, um diretório de arquivos servido pelo NGINX tecnicamente se qualifica como um proxy válido para módulos Go. Isso explica, em parte, por que os proxies se espalharam tão rapidamente pelo mundo do Go e por que tão poucos deles realizam qualquer verificação, a menos que você mesmo implemente isso.
A verificação no Go ocorre no lado do cliente, e não no proxy. O comando `go` compara o conteúdo do módulo baixado com o hash contido no `go.sum`. Quando esse hash ainda não está registrado, ele normalmente o obtém do banco de dados de checksums — um log independente do tipo “somente acréscimo” — antes de gravá-lo no `go.sum`. Uma vez registrado, as compilações posteriores simplesmente comparam com esse hash armazenado.
Portanto, as propriedades de segurança pelas quais a maioria das equipes dá crédito ao seu proxy são, na verdade, propriedades do go.sum e do GOSUMDB. Elas funcionam exatamente da mesma forma, quer você esteja executando um proxy ou acessando proxy.golang.org diretamente.
A vírgula e o pipe determinam o seu comportamento alternativo
O GOPROXY recebe uma lista de fontes, e o caractere que as separa determina o que acontece quando uma dessas fontes falha.
# Passa para a próxima entrada apenas nos códigos 404 ou 410
GOPROXY=https://goproxy.internal.example.com,direct
# Passa para a próxima entrada em CASO DE QUALQUER erro
GOPROXY=https://goproxy.internal.example.com|direto
Esse diagrama pressupõe que “direct” seja a próxima fonte na lista, da mesma forma que nos exemplos acima. Por si só, um pipe significa apenas que o fluxo segue para o que vier a seguir.
Uma vírgula indica ao comando `go` que só deve tentar a próxima fonte quando a atual informar categoricamente que o módulo está ausente, ou seja, em caso de erro 404 ou 410. Um pipe indica que ele deve tentar a próxima fonte diante de qualquer tipo de erro, incluindo tempos de espera, falhas de TLS e erros 500.
Imagine o que isso significa no meio de um incidente. Seu proxy interno sofre uma falha na implantação e começa a gerar erros 502. Com um “pipe” seguido de “direct”, todas as compilações da organização passam, discretamente, a buscar os módulos no controle de versão público.
| Separador | Não é válido para | Comportamento quando o proxy está fora do ar |
| Vírgula | Apenas 404 e 410 | Outros erros de proxy interrompem a resolução, fazendo com que as compilações falhem de forma evidente |
| Tubo | Qualquer erro | A resolução passa para a próxima fonte, de modo que uma conexão direta subsequente contorna discretamente o proxy |
Dica profissional: Verifique o que sua CI realmente tem configurado, e não o que o runbook afirma que está configurado. GOFLAGS e GOPROXY tendem a ser herdados das imagens base, e um pipe que alguém adicionou há dois anos para resolver uma implantação instável é exatamente o tipo de coisa que sobrevive a todas as revisões posteriores, porque as compilações continuam sendo aprovadas.
O parâmetro “direct” no final da maioria das strings do GOPROXY também merece uma análise à parte. Ele instrui a cadeia de ferramentas a recorrer ao controle de versão quando o proxy não encontrar nada; assim, um caminho de módulo que seu proxy nunca tenha visto ainda será resolvido diretamente a partir do repositório upstream, como de costume. Se o objetivo de configurar um proxy era ser a única fonte de verdade, o parâmetro “direct” silenciosamente anula isso.
GOPROXY=off é a opção mais restritiva disponível. O comando go não baixará nenhum módulo; portanto, tudo o que for necessário para a compilação já precisa estar no cache de módulos. Ao omitir o “direct” no final, você obtém um limite rígido para as fontes sem precisar ir tão longe assim.
Por que a hospedagem própria não verifica nada por conta própria
O Athens é um software confiável. Ele faz exatamente o que se espera de um módulo proxy.
O que ele não inclui é qualquer opinião sobre o que há dentro de um módulo. Não há nenhuma verificação de malware, nenhuma verificação de que o arquivo corresponda ao repositório de origem e nenhuma maneira pronta para uso de dizer “bloqueie este módulo, ele começou a fazer algo novo na v1.4.2”. A filtragem padrão funciona com base nos caminhos dos módulos, e não no que o código está fazendo. O Athens oferece um gancho de validação que é chamado a cada download de módulo, para que os operadores possam ampliá-lo com sua própria lógica de verificação ou de políticas, mas essa verificação é algo que você mesmo projeta, executa e mantém em funcionamento.
Essa lacuna é importante devido ao local onde o código malicioso em Go é executado. O Go não possui um gancho de script na hora da instalação, da mesma forma que o npm , o que é um grande ponto a seu favor. Mas o código malicioso em um módulo Go ainda é executado posteriormente, quando um aplicativo ou ferramenta que importa o pacote é executado, normalmente por meio de uma função init() que é acionada durante a inicialização do programa.
Um proxy que armazena em cache e disponibiliza esse módulo não fez nada de errado, de acordo com a especificação. Ele cumpriu exatamente a função para a qual foi criado.
A verdadeira lacuna está em saber se o arquivo fornecido por um proxy corresponde ao repositório de origem para o qual o caminho do módulo afirma apontar. Nada no protocolo exige que um zip de módulo corresponda a um commit específico, o que o torna um artefato, e não uma certificação. Recompilar um módulo a partir do código-fonte e comparar esse resultado com o que foi fornecido pelo caminho é um controle diferente, e isso precisa ocorrer antes que o artefato seja admitido em um repositório confiável ou fornecido a uma compilação.
Comparação entre cinco arquiteturas GOPROXY
Cinco formações cobrem praticamente tudo o que as equipes executam nos treinos. O que as diferencia é o que cada uma delas bloqueia e quem é responsável por executá-la.
| Arquitetura | Controle de disponibilidade | Exibe o conteúdo do módulo | Carga operacional |
| Diretamente da VCS | Nenhum, depende do host de origem | Não | Nenhum |
| Proxy público com fallback direto | Cache do Google mais VCS | Não | Nenhum |
| Proxy auto-hospedado, como o Athens | Você controla o cache | Não por padrão | Moderador, fica por sua conta |
| Proxy do gerenciador de repositório | Você controla o cache, além das regras de política | Varia de acordo com o produto e os complementos | Moderado |
| Registro verificado e reconstruído | Você controla o cache | Sim | Low, operado para você |
O proxy público com fallback direto é a configuração padrão com a qual a maioria das equipes começa, e é melhor do que sua reputação. proxy.golang.org mantém um cache imutável; assim, uma versão que já foi servida uma vez permanece disponível e inalterada, mesmo que o repositório de origem seja excluído. Essa é uma propriedade de segurança real, e a hospedagem própria não a reproduz automaticamente.
Muitas equipes acabam optando por um proxy auto-hospedado como o Athens, e a conclusão sincera é que ele proporciona disponibilidade e visibilidade, e não verificação.
Um proxy de gerenciador de repositório oferece controle de acesso centralizado e, dependendo do produto e dos complementos de política que você licenciar, pode auditar os componentes em relação às políticas e colocar em quarentena aqueles que não estiverem em conformidade. O que o proxy comum ainda não garante é que um artefato tenha sido recompilado a partir da fonte indicada em seu caminho e que corresponda a ela.
Um registro filtrado e recompilado constitui uma categoria distinta, e não uma variação das quatro primeiras. Ele recompila cada módulo a partir do código-fonte e o verifica em busca de malware antes de disponibilizá-lo; é por isso que essa é a única linha que responde “sim” na coluna de filtragem. Esse é o modelo SecureChain para Go foi desenvolvido, com a cobertura para Go prevista para o quarto trimestre de 2026.
De onde vem o abuso do cache de proxy
Os incidentes relacionados ao cache que afetam a infraestrutura do módulo Go tendem a seguir o mesmo padrão, e um caso ocorrido no início de 2025 ilustra bem isso. Em fevereiro daquele ano, pesquisadores descobriram um módulo chamado github.com/boltdb-go/bolt que se passava pelo BoltDB, um armazenamento de chave/valor incorporado amplamente utilizado que contava com mais de 8.000 pacotes dependentes quando o incidente foi relatado.
O agente malicioso havia publicado a versão maliciosa em novembro de 2021 e, em seguida, reescreveu a tag do GitHub para que ela apontasse para um commit limpo, o que significava que qualquer pessoa que auditasse o repositório manualmente não perceberia nada de errado. O proxy do módulo Go já havia armazenado em cache a versão maliciosa original e, como esse cache foi projetado para ser imutável, a cópia com backdoor permaneceu disponível por mais de três anos para qualquer cliente que resolvesse aquela versão do módulo, até que os pesquisadores finalmente a sinalizaram como um typosquat.
Não se tratou de uma falha na verificação de checksum. O caminho do módulo `boltdb-go/bolt` era distinto e falsificado; portanto, não havia um checksum confiável do BoltDB verdadeiro com o qual o falso precisasse corresponder. As verificações de integridade do Go não tiveram problema em autenticar os próprios bytes do typosquat, pois essa é a questão que elas se propõem a resolver. O backdoor teve sucesso na escolha do nome.
Essa distinção é importante, pois não se trata da mesma coisa que a confiança pura na primeira utilização. Para módulos públicos, o cliente normalmente consulta o banco de dados de checksums — um registro autenticado de forma independente — na primeira vez em que precisa de um hash para uma versão; assim, um proxy não pode alterar unilateralmente o valor confiável de uma versão que já esteja registrada. Um proxy auto-hospedado ainda decide qual artefato o cliente vê primeiro, mas, com o GOSUMDB instalado, é o banco de dados de checksums — e não o proxy — que decide se esse artefato será aceito.
O que nada disso faz é verificar se um módulo merece confiança, para começar, e é exatamente essa lacuna que um “typosquat” aproveita. E a parte pública desse mecanismo fica inativa para qualquer item correspondente a GOPRIVATE, ou em qualquer ambiente em que GOSUMDB esteja desativado.
| Cenário | As verificações de integridade do Go detectam isso |
| A versão em cache é modificada posteriormente no próprio local | Sim, incompatibilidade no go.sum |
| Primeira resolução de um módulo público por meio de um proxy comprometido | Normalmente sim, se o GOSUMDB estiver habilitado e tiver, ou puder obter, um registro para essa versão |
| Caminho do módulo correspondente a um padrão GOPRIVATE | Em parte, a consulta ao banco de dados público é ignorada por padrão, embora uma entrada existente no go.sum ainda detecte alterações posteriores |
| O proxy disponibiliza uma versão pública que o servidor de origem nunca publicou | Sim, nos casos em que se aplica a verificação da soma de verificação do banco de dados, já que o banco de dados não pode autenticar uma versão que não existe no upstream |
O que o GOPRIVATE desativa discretamente
O GOPRIVATE existe por um bom motivo. Módulos privados não devem ser solicitados a um proxy público, e seus caminhos também não devem ser enviados a um banco de dados público de checksum, já que o próprio caminho do módulo pode revelar informações sobre o que você está desenvolvendo.
É também uma variável de conveniência, e é nesse efeito colateral que as coisas dão errado. GOPRIVATE fornece o valor padrão tanto para GONOPROXY quanto para GONOSUMDB, conforme referência de módulos do Go, o que significa que todos os caminhos com os quais ele coincide são excluídos do proxy e do banco de dados público de checksums ao mesmo tempo, a menos que uma dessas duas variáveis seja definida separadamente.
É aqui que isso dá errado na prática.
GOPRIVATE=github.com/yourcompany/*
Se for específico o suficiente, ele corresponderá à organização que você pretendia selecionar.
GOPRIVATE=*.example.com,github.com/*
Esse segundo padrão exclui todos os módulos hospedados no GitHub do banco de dados público de checksums, o que abrange uma parcela enorme do que a maioria das compilações em Go obtém. É fácil que os padrões se expandam dessa forma: uma falha na resolução precisa ser corrigida, e flexibilizar a condição de correspondência é a alteração que faz com que a compilação seja aprovada imediatamente.
Verifique quais configurações estão realmente definidas em seus ambientes:
go env GOPROXY GOSUMDB GOPRIVATE GONOPROXY GONOSUMDB GOFLAGS
Execute isso em um servidor de CI, não apenas no seu laptop. Os resultados podem variar, pois o CI utiliza variáveis das imagens base e da configuração do servidor.
Defina-as deliberadamente. Configure GONOPROXY e GONOSUMDB diretamente quando seu roteamento e sua política de checksum precisarem ser diferentes, o que ocorre com mais frequência do que as equipes esperam. Se um proxy interno confiável atender tanto a módulos públicos quanto privados, GONOPROXY=none mantém todo o tráfego passando por esse proxy, enquanto GONOSUMDB abrange apenas os caminhos genuinamente privados. O que você não quer é o contrário: deixar caminhos privados na consulta ao banco de dados público, o que pode interromper a compilação ou vazar o caminho para um serviço público.
Escolhendo uma arquitetura
A decisão depende, em última análise, do problema que você está realmente tentando resolver, e ser honesto a respeito disso encurta consideravelmente todo o projeto.
Se o fator determinante for a disponibilidade e a reprodutibilidade da construção, execute um proxy auto-hospedado ou um gerenciador de repositório, use uma vírgula em vez de uma barra vertical e pare de chamá-lo de controle de segurança. Trata-se de uma boa infraestrutura que cumpre exatamente a função para a qual foi criada. Se o fator determinante for a visibilidade sobre os elementos dos quais a organização depende, praticamente qualquer proxy permite alcançar esse objetivo, e as opções de gerenciadores de repositório oferecem melhores relatórios com menor sobrecarga operacional.
Se o fator determinante for a aplicação de políticas sobre quais módulos são permitidos, um gerenciador de repositório com regras de caminho constitui a base, e é nos complementos de política ou firewall que a aplicação efetiva ocorre. O Athens pode ser direcionado nessa direção por meio de seu gancho de validação, mas a lógica, o ajuste e o pager ficam a seu cargo.
Se o driver estiver verificando se o conteúdo do módulo é confiável, o protocolo do proxy, por si só, não responde a essa questão, pois nunca foi desenvolvido com uma etapa de verificação. Esse controle precisa estar localizado ao redor do proxy ou vir de um sistema que valide os artefatos em relação à fonte antes de disponibilizá-los.
E se o Go fizer parte da sua pilha de tecnologias ao lado do npm, PyPI, Maven ou crates.io, defina a arquitetura uma única vez, para todos eles. Cinco proxies por ecossistema, administrados por cinco equipes diferentes, são exatamente o que leva ao surgimento de lacunas na cobertura. O PolinRider é um bom exemplo do porquê disso: a campanha ligada à Coreia do Norte rastreada sob esse nome vem publicando pacotes maliciosos no npm, no Packagist, nos módulos Go e nas extensões do Chrome, e ainda estava ativa quando os pesquisadores escreveram sobre o caso, em julho de 2026. O cluster mais amplo Contagious Interview, ao qual ela pertence, foi associado a mais de 1.700 pacotes maliciosos no npm, PyPI, Go e Rust. Um problema no nível da pilha precisa de uma solução no nível da pilha, não de cinco caches desconectados.
Um proxy é o lugar certo para resolver questões de disponibilidade, latência e reprodutibilidade. Não é, porém, o lugar adequado para lidar com questões de confiança. Manter esses dois objetivos separados mantém a arquitetura simples e permite que um proxy funcional seja lançado. Misturá-los transforma um projeto simples de cache em um esforço muito maior de engenharia de políticas e segurança, com o risco real de acabar com uma camada complexa de políticas acoplada ao que é, fundamentalmente, um componente de cache.
Onde o TuxCare SecureChain se encaixa
Há também um problema que nenhum proxy aborda: módulos que ninguém mais mantém. Um cache atende a um módulo inativo com exatamente a mesma fidelidade que a um módulo mantido ativamente, e um módulo sem patches, com um CVE conhecido e sem correção do desenvolvedor original, não é algo que uma arquitetura de cache resolva. Fazer o backport de uma correção para um código do qual o mantenedor se afastou é uma disciplina à parte, baseada em testes de regressão e compatibilidade comportamental, e é geralmente por isso que acaba ficando fora do âmbito de responsabilidade de uma equipe de plataforma.
Se o que falta na sua arquitetura é a verificação, essa é a lacuna SecureChain foi criado para preencher essa lacuna nos ecossistemas que geralmente fazem parte da mesma pilha. O npm já está disponível, Maven e PyPI chegam em agosto de 2026, e Go, Rust/crates.ioe Packagist/PHP seguirão no quarto trimestre de 2026. Em cada um deles, o SecureChain recompila pacotes a partir do código-fonte, os verifica no momento da obtenção antes que qualquer coisa chegue à compilação e continua corrigindo vulnerabilidades conhecidas mesmo depois que o desenvolvedor original do pacote deixar de fornecer atualizações. Quer saber como isso se encaixa na sua própria configuração do GOPROXY ou no restante da sua pilha de tecnologias? Envie-nos uma pergunta e a equipe irá orientá-lo passo a passo.
Perguntas frequentes
O que é um proxy de módulo Go?
Um servidor HTTP que implementa o protocolo de proxy de módulos do Go. Ele disponibiliza listas de versões, metadados, arquivos go.mod e arquivos-fonte para os caminhos dos módulos. Ele armazena em cache e encaminha, e o próprio protocolo não inclui nenhuma verificação do conteúdo que retorna.
O fato de eu ter meu próprio proxy de módulos Go torna as compilações mais seguras?
Isso melhora a disponibilidade, a reprodutibilidade e a visibilidade, todas características úteis. O protocolo do proxy não possui uma etapa de verificação própria. A autenticação do conteúdo ocorre no lado do cliente: o comando `go` verifica os downloads em relação ao `go.sum` e, para módulos públicos, em relação ao banco de dados de checksums quando ainda não há nenhum hash registrado. Isso funciona da mesma forma, independentemente de você estar executando um proxy ou não, a menos que o `GOSUMDB` esteja desativado ou que o caminho esteja excluído do banco de dados público por meio de `GOPRIVATE` ou `GONOSUMDB`.
Qual é a diferença entre uma vírgula e uma barra vertical no GOPROXY?
Uma vírgula faz com que o comando `go` passe para a próxima fonte apenas em caso de erro 404 ou 410; portanto, qualquer outro erro de proxy interrompe a resolução. Um pipe faz com que ele passe para a próxima fonte em caso de qualquer erro, o que significa que uma interrupção no proxy pode enviar discretamente as compilações para a internet pública, em vez de simplesmente apresentar uma falha.
O GOPRIVATE desativa a verificação da soma de verificação?
Por padrão, ele ignora a consulta ao banco de dados público de checksums para caminhos correspondentes, pois o GOPRIVATE alimenta o GONOSUMDB. Uma entrada existente no go.sum ainda pode detectar uma alteração posterior no conteúdo.
Atenas é suficiente para um proxy de módulo Go privado?
Para disponibilidade, armazenamento em cache e distribuição interna de módulos, sim. Sua filtragem padrão atua nos caminhos dos módulos, e não no conteúdo; portanto, por padrão, ela não indica se um módulo é malicioso ou se o arquivo que ele está servindo corresponde ao repositório de origem. Seu gancho de validação pode ser ampliado com esse tipo de lógica, mas você mesmo deve projetar e operar essa parte.
Como posso verificar em que minhas compilações do Go realmente confiam?
Execute o comando `go env GOPROXY GOSUMDB GOPRIVATE GONOPROXY GONOSUMDB GOFLAGS` em um executador de CI. A configuração herdada das imagens base e das configurações do executador frequentemente difere das configurações locais de desenvolvimento e do que o runbook indica que deveria ser.



