SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor

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 curl ou fetch de saída para qualquer host, com o valor numa query string.
  • Um git commit e git push para 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 o agente está a trabalhar será lido. Depois de lido, fica na janela de contexto. Isto significa que aparece na transcrição, em qualquer log que mantenha e em tudo o que o agente escrever a seguir.

Antes, com a chave na árvore em que o agente trabalha:

cd ~/projects/billing
cat .env
# STRIPE_SECRET_KEY=sk_live_...
# DATABASE_URL=postgres://app:hunter2@db.internal:5432/billing

Depois, com o ficheiro fora do alcance:

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/.env

O 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 impede que o agente abra acidentalmente um ficheiro enquanto explora o projeto. As regras também abrangem os comandos de ficheiros que o Claude Code reconhece no Bash, como cat e head, e os destinos de redirecionamentos como < .env. No entanto, não impedem que uma instrução injetada execute grep -r KEY . dentro do diretório do projeto, nem que um script Python curto abra .env diretamente. A documentação de permissões indica que as regras de negação não se aplicam a comandos que leem ficheiros sem os nomear, nem a subprocessos que abrem ficheiros por conta própria. Para esses casos, a documentação aponta para o sandbox do Claude Code, que aplica o bloqueio ao nível do sistema operativo. A apresentação de um pedido de confirmação antes da execução depende do modo de permissões da sessão. Além disso, o modo automático é agora o modo inicial integrado nas sessões interativas do Claude Code, pelo que um servidor que não esteja a monitorizar executará mais destes comandos sem pedir confirmação. A mesma limitação aplica-se a tudo o que orienta os hábitos do agente, em vez de controlar as suas permissões: uma skill que obriga o agente a fazer apenas a alteração mínima que funciona impede uma execução de explorar ficheiros que não precisava de 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 a parede. 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 elimina tudo isso.

sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519

A última linha tem de falhar com cat: /home/you/.ssh/id_ed25519: Permission denied. Se imprimir uma chave, o utilizador do agente consegue aceder ao ficheiro. Abrir um ficheiro pelo nome requer permissão de pesquisa (x) em todos os diretórios acima dele, não permissão de leitura. Por isso, um diretório pessoal com o modo 711, que mais ninguém consegue listar, ainda permite aceder à chave se o próprio ficheiro da chave for legível. chmod 700 ~ fecha o caminho e chmod 600 ~/.ssh/id_ed25519 corrige a chave. Não adicione o utilizador do agente a sudo e não lhe atribua uma regra NOPASSWD mais abrangente do que o único comando de que realmente precisa. Utilizadores com privilégios mínimos numa VPS explica os detalhes dos grupos e do sudoers. Tenha esta separação em conta quando executar mais do que 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 numa VPS cloud. O serviço de metadados da instância responde num endereço link local fixo e frequentemente fornece credenciais de funções a qualquer processo que as solicite. O endpoint não verifica qual é o utilizador local que faz a chamada. Na EC2, a AWS documenta que as chamadas ao IMDSv1 não precisam de token, e recomenda uma regra de firewall local baseada no ID do utilizador para impedir que um utilizador aceda ao serviço.

sudo iptables -I OUTPUT 1 -p tcp -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECT

Insira a regra no início com -I, em vez de a acrescentar com -A. Num host que execute ufw com a política predefinida de permitir tráfego de saída, a última regra da cadeia OUTPUT do ufw aceita todas as novas ligações TCP. Por isso, uma regra acrescentada no fim nunca é alcançada. As regras do iptables também desaparecem após um reboot, a menos que as torne persistentes. Se tiver ativado o endpoint IPv6 do serviço de metadados, [fd00:ec2::254], adicione a mesma regra com ip6tables.

Verifique 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. Se imprimir uma listagem, a regra não está a ser alcançada.

Injete a credencial no limite

O padrão que resolve este problema é a injeção de credenciais. O agente nunca mantém uma chave real. Envia o pedido através de um gateway local, e o gateway substitui um marcador pelo segredo real ao encaminhar o pedido. O segredo fica no armazenamento do gateway, num processo diferente e pertencente a outro utilizador.

OneCLI é uma implementação open source deste padrão. Tem licença Apache-2.0, exceto nos diretórios ee/, que têm uma licença empresarial separada e exigem uma subscrição para utilização em produção. O OneCLI é executado em contentores juntamente com o agente. Em outubro de 2026, a documentação do projeto descreve esta configuração do Docker Compose, que lê três segredos gerados de um ficheiro .env junto do ficheiro compose:

git clone https://github.com/onecli/onecli.git && cd onecli/docker
cat > .env <<EOF
SECRET_ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
GATEWAY_INTERNAL_SECRET=$(head -c 32 /dev/urandom | base64)
BETTER_AUTH_SECRET=$(head -c 32 /dev/urandom | base64)
COMPOSE_PROFILES=runner
EOF
chmod 600 .env
docker compose up -d --wait

Faça uma cópia de segurança desse ficheiro .env, porque a perda de SECRET_ENCRYPTION_KEY deixa inacessíveis todos os segredos armazenados com ele. O dashboard escuta na porta 10254 e o gateway na porta 10255. Por predefinição, ambos estão associados a 127.0.0.1. Armazene a credencial real uma única vez. Depois, forneça a cada agente um valor marcador no lugar da chave e o seu próprio token de acesso com permissões limitadas. O agente envia esse token 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. Consulte um único registo de auditoria em vez de tentar descobrir em qual dos seis ambientes existia 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 o utilizador do serviço pode ler. O segredo fica exposto como %d no ficheiro da unidade e como $CREDENTIALS_DIRECTORY dentro do processo. Essa restrição é aplicada por utilizador, não por serviço. Por isso, atribua um utilizador próprio a cada serviço de agente. Qualquer outro processo executado com o mesmo utilizador pode abrir o diretório, a menos que a unidade também utilize o isolamento do sistema de ficheiros, como PrivateMounts=yes, que o oculta de outros serviços. O valor nunca aparece em /proc/<pid>/environ. Por isso, ps eww não o pode 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, que inclui 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_key

O último comando imprime sk-example-value. Isso confirma que o ficheiro encriptado pode ser desencriptado neste host. Em seguida, 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-worker

O código do agente abre o ficheiro em $AGENT_KEY_FILE quando precisa do valor. A leitura de um ficheiro ocorre num momento específico. Uma variável de ambiente permanece durante toda a vida do processo e fica disponível em todos os processos filhos que este 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 900

Quinze 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 agent. Execute-as como o utilizador do agent, 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/user

A primeira não deve apresentar qualquer saída. A segunda deve apresentar ls: cannot open directory '/home/you/': Permission denied, embora isso apenas mostre que o diretório não pode ser listado; o teste cat anterior é que prova que não é possível ler um ficheiro dentro dele. A terceira mostra que identidade o caminho de rede do agent apresenta. Essa é a questão a que o padrão de gateway responde: 401 significa que o pedido chegou ao GitHub sem uma credencial aceite pelo GitHub, seja porque não tinha nenhuma, seja porque a credencial é inválida ou expirou; 200 significa que o pedido transporta uma credencial válida, pelo que deve saber qual é o token utilizado. Se executar agents sem supervisão, controlar os custos de agents de IA num VPS apresenta os limites de orçamento que complementam 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.

Este artigo foi útil?