Como manter segredos fora dos agentes de IA
Agentes podem enviar chaves de API em uma chamada de ferramenta. Use tokens com escopo e curta duração via gateway de credenciais, nunca as chaves reais.
O que significa manter segredos fora dos agentes de IA
Um agente de IA é um processo Linux normal que executa comandos. Todas as variáveis de ambiente que esse processo mantém podem ser lidas pelo código que ele executa. Portanto, uma chave de API no ambiente do agente pode ser enviada pelo agente para qualquer host que consiga alcançar. Manter os segredos fora do agente significa fornecer-lhe um identificador em vez da chave: um token com escopo limitado e curta duração ou um marcador que outro componente substitui pelo valor real no limite da rede.
Isto não é uma história sobre um modelo se tornar hostil. O mecanismo é mais simples. Um agente lê uma página Web, um README ou um comentário de issue que contém instruções e segue-as, porque, para um modelo de linguagem, não há diferença entre o texto que escreveu e o texto que obteve. Isto é prompt injection. Quando isso acontece, os danos ficam limitados por uma única coisa: aquilo que o processo consegue ler. Se ainda não definiu um limite, executar um agente de programação com segurança num servidor descreve os níveis de isolamento sobre os quais este guia se baseia.
O modelo de ameaça em termos simples
Execute isto com o utilizador usado pelo seu agente.
tr '\0' '\n' < /proc/self/environ | grep -iE 'key|token|secret|password'Cada linha apresentada corresponde a um pedido HTTP até ao servidor de um desconhecido. Agora veja o que existe no disco perto do agente.
grep -rIl --exclude-dir=.git -e 'API_KEY' -e 'SECRET' ~/projects
find ~ -maxdepth 3 -name '.env' -o -name 'credentials' -o -name '*.pem'Um agente com uma shell não precisa de uma exploração sofisticada para retirar esses dados. Quatro caminhos comuns são suficientes, e os quatro parecem trabalho normal no log:
- Um
curloufetchde saída para qualquer host, com o valor numa query string. - Um
git commitegit pushpara um repositório onde o agente possa escrever. - Um script de instalação de pacote, que executa código arbitrário como o utilizador do agente.
- Uma consulta DNS a um hostname que contenha o valor. Essa consulta sai mesmo quando o tráfego HTTP de saída está bloqueado.
Não é possível resolver isto apenas com revisão. A correção é garantir que não existem dados valiosos ao alcance.
Um segredo na árvore de trabalho é um segredo na janela de contexto
Um agente lê ficheiros. Um ficheiro .env no repositório em que está a trabalhar será lido. Depois de lido, fica na janela de contexto. Isto significa que fica na transcrição, em qualquer log que mantenha e em tudo o que o agente escrever a seguir.
Antes, com a chave na árvore de trabalho do agente:
cd ~/projects/billing
cat .env
# STRIPE_SECRET_KEY=sk_live_...
# DATABASE_URL=postgres://app:hunter2@db.internal:5432/billingDepois, com o ficheiro fora do alcance do agente:
sudo install -d -m 750 -o root -g agent-review /etc/agent-review
sudo install -m 640 -o root -g agent-review ~/projects/billing/.env /etc/agent-review/billing.env
rm ~/projects/billing/.envO utilizador do agente já não consegue abrir o ficheiro, porque a árvore de trabalho deixou de o conter. As regras de negação na própria configuração do agente são uma segunda camada, não a primeira. O Claude Code lê as regras de permissões a partir de .claude/settings.json no projeto:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./secrets/**)", "Read(./**/*.pem)"]
}
}Isto evita que o agente abra acidentalmente um ficheiro enquanto explora o projeto. Não impede uma instrução injetada de executar base64 .env, porque isso é um comando de shell, não uma leitura de ficheiro. O facto de ser pedida confirmação antes da execução desse comando depende do modo de permissões da sessão. O modo automático passa a ser o modo predefinido do Claude Code em agosto de 2026. Por isso, um servidor que não esteja a monitorizar executará mais comandos desse tipo sem solicitar confirmação. O mesmo limite aplica-se a tudo o que molda os hábitos do agente, mas não as suas permissões. Uma skill que obriga o agente a fazer apenas a alteração mínima que funciona impede que uma execução percorra ficheiros que não deveria abrir, mas continua a ser uma recomendação que pode ser contrariada pelo modelo. Trate a configuração como uma barreira de proteção e as permissões do sistema de ficheiros como o muro. A mesma separação aplica-se dentro de contentores: ficheiros env e segredos no Docker Compose aborda a versão deste problema uma camada abaixo.
Dê a cada agente o seu próprio utilizador sem privilégios
Se o agente for executado como o seu utilizador, herdará as suas chaves SSH, as suas credenciais da cloud e o histórico da sua shell. Um utilizador separado requer um comando e remove tudo isso.
sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519A última linha tem de falhar com cat: /home/you/.ssh/id_ed25519: Permission denied. Se imprimir uma chave, o seu diretório pessoal pode ser lido pelo grupo ou por outros utilizadores, e chmod 700 ~ corrige isso. Não adicione o utilizador do agente a sudo nem lhe atribua uma regra NOPASSWD mais abrangente do que o único comando de que realmente precisa. Utilizadores com privilégios mínimos num VPS explica os detalhes dos grupos e do sudoers. Mantenha essa separação quando executar mais de uma sessão no servidor, porque uma sessão do Claude Code pode enviar texto diretamente para outra, e tudo o que a primeira sessão tiver pode atravessar esse canal numa única mensagem.
Vale a pena acrescentar mais um limite num VPS na cloud. O serviço de metadados da instância responde num endereço link-local fixo e frequentemente fornece credenciais da role a qualquer processo que as solicite.
sudo iptables -A OUTPUT -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECTVerifique isso a partir do lado do agente. sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ não deve imprimir nada e deve terminar com um código diferente de zero, porque o pacote é rejeitado antes de sair do servidor.
Injete a credencial na fronteira
O padrão que resolve este problema é a injeção de credenciais. O agente nunca tem uma chave real. Envia o pedido através de um gateway local, e o gateway substitui um marcador pelo segredo real antes de o pedido sair. O segredo fica armazenado no gateway, num processo diferente, pertencente a outro utilizador.
OneCLI é uma implementação open source deste padrão, licenciada ao abrigo da Apache-2.0, e funciona como um contentor junto do agente. Em julho de 2026, o projeto documenta esta configuração:
git clone https://github.com/onecli/onecli.git
cd onecli
docker compose -f docker/docker-compose.yml up -d --waitO dashboard escuta na porta 10254 e o gateway na porta 10255. Armazena a credencial real uma única vez. Depois, fornece a cada agente um valor marcador em vez da chave, juntamente com o seu próprio token de acesso limitado, que envia num cabeçalho Proxy-Authorization. O gateway identifica o pedido de saída pelo host e pelo caminho, desencripta a credencial correspondente e substitui o marcador. O ambiente do agente não contém nada que valha a pena roubar.
O valor desta abordagem não está na encriptação. Está no facto de a pergunta "o que este agente utilizou e quando" passar a ser uma consulta aos logs. Consulta um único registo de auditoria em vez de tentar adivinhar qual das seis configurações continha uma cópia da chave.
Entregue o segredo ao processo, não ao ambiente
Se executar o agente com systemd, não precisa de variáveis de ambiente. LoadCredential= coloca o segredo num diretório privado que apenas esse serviço pode ler. O segredo fica exposto como %d no ficheiro da unidade e como $CREDENTIALS_DIRECTORY dentro do processo. O valor nunca aparece em /proc/<pid>/environ. Por isso, ps eww não o consegue mostrar. O diretório desaparece quando o serviço para.
Encripte primeiro a credencial para a máquina. Estes comandos vêm da documentação do systemd e funcionam no systemd 250 ou posterior, incluindo Ubuntu 24.04 e Debian 13:
echo -n 'sk-example-value' > /tmp/plain.txt
sudo systemd-creds encrypt --name=api_key /tmp/plain.txt /etc/credstore/api_key.cred
shred -u /tmp/plain.txt
sudo systemd-run -P --wait -p LoadCredentialEncrypted=api_key:/etc/credstore/api_key.cred \
systemd-creds cat api_keyO último comando apresenta sk-example-value. Isso confirma que o ficheiro encriptado pode ser desencriptado neste host. Depois, faça referência a ele na unidade:
[Service]
User=agent-review
LoadCredentialEncrypted=api_key:/etc/credstore/api_key.cred
Environment=AGENT_KEY_FILE=%d/api_key
ExecStart=/usr/local/bin/agent-workerO código do agente abre o ficheiro em $AGENT_KEY_FILE quando precisa do valor. A leitura de um ficheiro ocorre apenas nesse momento. Uma variável de ambiente permanece durante toda a vida do processo e fica disponível em cada processo filho que ele iniciar.
Prefira tokens de curta duração a chaves de longa duração
Uma chave que nunca expira continua válida quando aparece, meses mais tarde, num log ou numa transcrição. Quando o serviço disponibilizar um token de sessão, use-o e defina o período de validade mais curto que o trabalho permitir.
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/agent-readonly \
--role-session-name agent-review \
--duration-seconds 900Quinze minutos é o mínimo aceite pelo AWS STS (security token service) e normalmente é suficiente para uma tarefa de um agente. No GitHub, atribua ao utilizador do agente o seu próprio gh login com um token de permissões granulares limitado ao único repositório em que trabalha, para que gh auth token dentro dessa sessão devolva algo que não possa aceder a mais nada. Limite primeiro por recurso e depois por tempo.
Verifique e continue a verificar
Vale a pena executar três verificações depois de qualquer alteração na configuração de um agente. Execute-as como o utilizador do agente, não como o seu próprio utilizador.
sudo -u agent-review env | grep -iE 'key|token|secret'
sudo -u agent-review ls -la /home/you/ 2>&1 | head -3
sudo -u agent-review curl -s -o /dev/null -w '%{http_code}\n' --max-time 5 https://api.github.com/userA primeira não deve mostrar nada. A segunda deve mostrar ls: cannot open directory '/home/you/': Permission denied. A terceira indica qual é a identidade apresentada pelo caminho de rede do agente. Essa é a questão que o padrão de gateway deve responder: 401 significa que o agente não está a transportar nenhuma credencial própria do GitHub, enquanto 200 significa que está a transportar uma. Nesse caso, deve saber qual é o token. Se executar agentes sem supervisão, controlar os custos de agentes de IA num VPS aborda os limites de orçamento que acompanham estes limites de acesso.
FAQ
Posso simplesmente confiar no modelo para não divulgar as minhas chaves?
Não, porque o modelo não é o atacante neste modelo de ameaça. O agente lê texto de páginas Web, repositórios e sistemas de acompanhamento de issues, e esse texto pode conter instruções. O modelo não tem uma forma fiável de distinguir as suas instruções do texto que obteve. Qualquer controlo que dependa de o modelo escolher corretamente falha na primeira vez que uma instrução injetada for convincente. Por isso, o controlo tem de estar no sistema operativo ou na rede.
As variáveis de ambiente são mesmo assim tão más para os segredos do agente?
São problemáticas de uma forma específica: são herdadas. Todos os processos filho que o agente inicia recebem uma cópia, incluindo um script de compilação, um executor de testes e qualquer hook de instalação de pacotes. As variáveis também podem ser lidas através de /proc/<pid>/environ pelo mesmo utilizador. Assim, qualquer processo iniciado pelo agente pode lê-las sem que o agente as passe explicitamente. Um ficheiro lido no momento da utilização, com LoadCredential= ou através de um gateway, limita a exposição a esse momento.
Colocar os segredos num vault resolve isto por si só?
Resolve apenas em parte. Um vault resolve o armazenamento. Se alojar esse vault por conta própria, precisa da sua própria revisão de hardening, porque um servidor Vaultwarden é normalmente comprometido através do seu token de administrador ou do seu ficheiro de backup, e não através dos itens encriptados que armazena. Isto não resolve o último passo, em que algo obtém o segredo do vault e o entrega ao agente como variável de ambiente. Esse processo coloca-o novamente na situação inicial. O que importa é quem faz a substituição. Se o agente obtém o segredo, o agente tem o segredo. Se um gateway ou o sistema init fizer a substituição fora do processo do agente, o agente nunca o terá.
Como posso saber se um agente já divulgou alguma coisa?
Normalmente, não é possível saber depois do acontecimento. Esse é o motivo para utilizar um gateway. Sem um gateway, as evidências ficam dispersas pelo histórico da shell, pela transcrição do agente e pelos logs de ligações de saída, que provavelmente não está a manter. Com um gateway de credenciais, cada utilização de uma credencial corresponde a uma linha com a identidade do agente e um timestamp. Se suspeitar de uma fuga, faça primeiro a rotação da chave e investigue depois. A rotação é barata, mas a certeza não é.
O que devo fazer hoje, no mínimo?
Retire todos os ficheiros .env dos diretórios onde os seus agentes trabalham e crie um utilizador sem privilégios para cada agente. Estas duas alterações demoram cerca de dez minutos e eliminam o caminho mais comum: um agente ler um ficheiro de credenciais que não tinha motivo para estar junto do código. O gateway e os tokens de curta duração são o passo seguinte, não o primeiro. O mesmo ponto de partida aplica-se a qualquer runtime de agente, incluindo executar um agente autónomo em segurança num VPS.