O Mecanismo de Segurança SSH Mais Subestimado: Por Que known_hosts Não É Apenas um Cache
O arquivo known_hosts do SSH, frequentemente descartado como um cache temporário, é na verdade uma base de identidades de servidores confiáveis. Este artigo explora sua importância e a falta de ferramentas para gerenciá-lo.
MundiX News·07 de julho de 2026·6 min de leitura·👁 1 views
Todo aquele que já se conectou a um novo servidor via SSH está familiarizado com o ritual interativo:
A autenticidade do host 'exemplo.com' não pode ser estabelecida.
O fingerprint da chave ED25519 é SHA256:...
Tem certeza de que deseja continuar a conexão (sim/não)?
E quase todos respondem mecanicamente com o mesmo comando:
yes
Este modelo é conhecido como TOFU (Trust On First Use) – Confiança no Primeiro Uso. Meses ou até anos depois, uma mensagem completamente diferente pode surgir no mesmo endereço:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
AVISO: IDENTIFICAÇÃO DO HOST REMOTO FOI ALTERADA!
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
E, na maioria dos casos, o cenário a seguir é igualmente simples e ineficaz:
ssh-keygen -R exemplo.com
ssh exemplo.com
yes
Pronto, o problema está resolvido e você pode voltar a não se preocupar. Ou será que não?
Até recentemente, eu tratava o arquivo known_hosts de forma semelhante. Apenas um cache de serviço do SSH. Se algo desse errado, eu removia a entrada, me conectava novamente e seguia em frente. No entanto, um dia, me deparei com um pensamento simples e óbvio: known_hosts não é um cache. É uma base de identidades de servidores confiáveis. Quando o SSH pergunta: "Você tem certeza de que deseja confiar neste servidor?", ele salva sua resposta exatamente no known_hosts. E, por anos, utiliza este arquivo como a única fonte de verdade para determinar se você está falando com o mesmo servidor ou com alguém completamente diferente. Isso cria uma situação peculiar: um dos mecanismos de segurança mais importantes do SSH reside em um arquivo de texto simples, com pouquíssimas ferramentas ao seu redor.
Onde o OpenSSH Termina
Vamos analisar o que o OpenSSH é capaz de fazer com sua própria base de confiança. Ele pode adicionar automaticamente uma nova entrada na primeira conexão, remover uma entrada (ssh-keygen -R), encontrar uma entrada (ssh-keygen -F) e hashear o arquivo (ssh-keygen -H). E, em grande parte, suas atribuições terminam aí. Ao gerenciar dezenas ou centenas de servidores, o known_hosts acumula centenas de entradas ao longo do tempo. E, de repente, percebe-se que o OpenSSH oferece pouca ajuda para responder a perguntas comuns: Quais servidores não existem mais? Quais chaves foram alteradas? Existem duplicatas? Quais entradas utilizam algoritmos obsoletos? Em quem eu confio neste exato momento? Geralmente, a resposta envolve grep, awk, sed ou uma série de scripts shell. Isso me surpreendeu profundamente. O SSH existe há mais de trinta anos e é um dos protocolos de rede mais difundidos no mundo. No entanto, seu modelo de confiança praticamente se encerra após o primeiro "yes". Criar confiança é fácil; mantê-la, já é outra história.
Como o SSH Obtém a Chave do Host?
Fiquei curioso sobre como o cliente obtém a chave pública do servidor. Sempre pensei que isso exigiria o estabelecimento de uma sessão SSH completa, negociação de criptografia, autenticação e, só então, a obtenção da informação necessária. Acontece que o processo é muito mais direto. Durante a conexão, a sequência é aproximadamente a seguinte:
Cliente
Servidor
TCP connect
->
<- Version banner
Version banner ->
<- SSH_MSG_KEXINIT
SSH_MSG_KEXINIT ->
SSH_MSG_KEX_ECDH_INIT ->
<- SSH_MSG_KEX_ECDH_REPLY
Inicialmente, as partes trocam banners de versão, como "SSH-2.0-OpenSSH_10.0". Em seguida, negociam os algoritmos suportados. Finalmente, o cliente envia SSH_MSG_KEX_ECDH_INIT. O mais interessante ocorre na mensagem de resposta: SSH_MSG_KEX_ECDH_REPLY contém a chave do host do servidor, uma chave pública temporária do servidor e uma assinatura criptográfica. É aqui que o cliente calcula o fingerprint, compara-o com o known_hosts e decide se continua a conexão ou não. O que mais me surpreendeu é que tudo isso acontece antes da autenticação do usuário. Sem senhas, sem chaves privadas, sem shell, sem uma sessão SSH aberta. E isso é totalmente lógico. A chave do host existe precisamente para que o cliente possa verificar com quem está falando antes de enviar quaisquer segredos. Portanto, se o objetivo é apenas verificar a identidade do servidor, a conexão pode ser encerrada imediatamente após o recebimento de SSH_MSG_KEX_ECDH_REPLY. Tudo o que é necessário já foi obtido.
Da Pesquisa à Ferramenta
Quando percebi isso, outra coisa ficou clara: para verificar a chave do host de um servidor, como mencionado anteriormente, não é necessário um cliente SSH completo. Não é preciso um shell, nem autenticação, nem a maior parte do SSH. Apenas um pequeno pedaço do protocolo é suficiente. Foi a partir dessa observação que gradualmente nasceu um pequeno projeto CLI que chamei de khm (known hosts manager). Inicialmente, foi escrito para uso pessoal. Eu queria ter a capacidade de responder rapidamente às perguntas que o OpenSSH, por algum motivo, não respondia. Por exemplo: As chaves de servidores já conhecidos foram alteradas? Há lixo ou duplicatas no known_hosts? Qual a diferença entre dois arquivos após a migração da infraestrutura ou rotação de chaves? O que eu realmente tenho hoje? Gradualmente, tornou-se evidente que o problema era muito mais amplo do que parecia inicialmente. Surpreendentemente, muitas pessoas veem o known_hosts como um arquivo temporário que pode ser excluído ao primeiro erro, quando, na verdade, é ele quem determina em quais servidores seu computador confia no momento. Por exemplo, esta é a saída que sempre senti falta no OpenSSH:
$ khm verify --all
OK github.com
OK gitlab.com
CHANGED old.example.com
UNREACHABLE backup.example.com
4 hosts • 2 OK • 1 changed • 1 unreachable
Não é uma lista de chaves, nem uma saída grep burra e suscetível a erros humanos, mas uma resposta simples a uma pergunta simples: "Ainda corresponde ao que eu confiava antes?"
Em Conclusão
Enquanto trabalhava neste projeto, a descoberta mais inesperada não foi o protocolo SSH em si. Fiquei surpreso com a atitude em relação ao known_hosts. Incluindo a minha. Acostumamo-nos a considerá-lo um cache temporário, que pode ser excluído sem remorso ao primeiro aviso do SSH. Mas, se olharmos mais de perto, é a única base local de identidades de servidores confiáveis utilizada pelo OpenSSH. E, assim que começamos a vê-lo dessa forma, torna-se surpreendente o quão poucas ferramentas existem ao seu redor. É por isso que o khm surgiu. Não como mais um cliente SSH, nem como substituto do OpenSSH. Mas como uma tentativa de tornar um pouco mais conveniente uma parte pequena, mas, a meu ver, injustamente esquecida, de seu modelo de confiança. O código-fonte do projeto, compilações binárias e documentação estão disponíveis no repositório: https://github.com/casablanque-code/khm
🛡️⚡
Pare de pesquisar. Comece a hackear.
O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.
Sem cartão para começar · Planos a partir de R$49/mês
Todo aquele que já se conectou a um novo servidor via SSH está familiarizado com o ritual interativo:
A autenticidade do host 'exemplo.com' não pode ser estabelecida.
O fingerprint da chave ED25519 é SHA256:...
Tem certeza de que deseja continuar a conexão (sim/não)?
E quase todos respondem mecanicamente com o mesmo comando:
yes
Este modelo é conhecido como TOFU (Trust On First Use) – Confiança no Primeiro Uso. Meses ou até anos depois, uma mensagem completamente diferente pode surgir no mesmo endereço:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
AVISO: IDENTIFICAÇÃO DO HOST REMOTO FOI ALTERADA!
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
E, na maioria dos casos, o cenário a seguir é igualmente simples e ineficaz:
ssh-keygen -R exemplo.com
ssh exemplo.com
yes
Pronto, o problema está resolvido e você pode voltar a não se preocupar. Ou será que não?
Até recentemente, eu tratava o arquivo known_hosts de forma semelhante. Apenas um cache de serviço do SSH. Se algo desse errado, eu removia a entrada, me conectava novamente e seguia em frente. No entanto, um dia, me deparei com um pensamento simples e óbvio: known_hosts não é um cache. É uma base de identidades de servidores confiáveis. Quando o SSH pergunta: "Você tem certeza de que deseja confiar neste servidor?", ele salva sua resposta exatamente no known_hosts. E, por anos, utiliza este arquivo como a única fonte de verdade para determinar se você está falando com o mesmo servidor ou com alguém completamente diferente. Isso cria uma situação peculiar: um dos mecanismos de segurança mais importantes do SSH reside em um arquivo de texto simples, com pouquíssimas ferramentas ao seu redor.
Onde o OpenSSH Termina
Vamos analisar o que o OpenSSH é capaz de fazer com sua própria base de confiança. Ele pode adicionar automaticamente uma nova entrada na primeira conexão, remover uma entrada (ssh-keygen -R), encontrar uma entrada (ssh-keygen -F) e hashear o arquivo (ssh-keygen -H). E, em grande parte, suas atribuições terminam aí. Ao gerenciar dezenas ou centenas de servidores, o known_hosts acumula centenas de entradas ao longo do tempo. E, de repente, percebe-se que o OpenSSH oferece pouca ajuda para responder a perguntas comuns: Quais servidores não existem mais? Quais chaves foram alteradas? Existem duplicatas? Quais entradas utilizam algoritmos obsoletos? Em quem eu confio neste exato momento? Geralmente, a resposta envolve grep, awk, sed ou uma série de scripts shell. Isso me surpreendeu profundamente. O SSH existe há mais de trinta anos e é um dos protocolos de rede mais difundidos no mundo. No entanto, seu modelo de confiança praticamente se encerra após o primeiro "yes". Criar confiança é fácil; mantê-la, já é outra história.
Como o SSH Obtém a Chave do Host?
Fiquei curioso sobre como o cliente obtém a chave pública do servidor. Sempre pensei que isso exigiria o estabelecimento de uma sessão SSH completa, negociação de criptografia, autenticação e, só então, a obtenção da informação necessária. Acontece que o processo é muito mais direto. Durante a conexão, a sequência é aproximadamente a seguinte:
Cliente
Servidor
TCP connect
->
<- Version banner
Version banner ->
<- SSH_MSG_KEXINIT
SSH_MSG_KEXINIT ->
SSH_MSG_KEX_ECDH_INIT ->
<- SSH_MSG_KEX_ECDH_REPLY
Inicialmente, as partes trocam banners de versão, como "SSH-2.0-OpenSSH_10.0". Em seguida, negociam os algoritmos suportados. Finalmente, o cliente envia SSH_MSG_KEX_ECDH_INIT. O mais interessante ocorre na mensagem de resposta: SSH_MSG_KEX_ECDH_REPLY contém a chave do host do servidor, uma chave pública temporária do servidor e uma assinatura criptográfica. É aqui que o cliente calcula o fingerprint, compara-o com o known_hosts e decide se continua a conexão ou não. O que mais me surpreendeu é que tudo isso acontece antes da autenticação do usuário. Sem senhas, sem chaves privadas, sem shell, sem uma sessão SSH aberta. E isso é totalmente lógico. A chave do host existe precisamente para que o cliente possa verificar com quem está falando antes de enviar quaisquer segredos. Portanto, se o objetivo é apenas verificar a identidade do servidor, a conexão pode ser encerrada imediatamente após o recebimento de SSH_MSG_KEX_ECDH_REPLY. Tudo o que é necessário já foi obtido.
Da Pesquisa à Ferramenta
Quando percebi isso, outra coisa ficou clara: para verificar a chave do host de um servidor, como mencionado anteriormente, não é necessário um cliente SSH completo. Não é preciso um shell, nem autenticação, nem a maior parte do SSH. Apenas um pequeno pedaço do protocolo é suficiente. Foi a partir dessa observação que gradualmente nasceu um pequeno projeto CLI que chamei de khm (known hosts manager). Inicialmente, foi escrito para uso pessoal. Eu queria ter a capacidade de responder rapidamente às perguntas que o OpenSSH, por algum motivo, não respondia. Por exemplo: As chaves de servidores já conhecidos foram alteradas? Há lixo ou duplicatas no known_hosts? Qual a diferença entre dois arquivos após a migração da infraestrutura ou rotação de chaves? O que eu realmente tenho hoje? Gradualmente, tornou-se evidente que o problema era muito mais amplo do que parecia inicialmente. Surpreendentemente, muitas pessoas veem o known_hosts como um arquivo temporário que pode ser excluído ao primeiro erro, quando, na verdade, é ele quem determina em quais servidores seu computador confia no momento. Por exemplo, esta é a saída que sempre senti falta no OpenSSH:
$ khm verify --all
OK github.com
OK gitlab.com
CHANGED old.example.com
UNREACHABLE backup.example.com
4 hosts • 2 OK • 1 changed • 1 unreachable
Não é uma lista de chaves, nem uma saída grep burra e suscetível a erros humanos, mas uma resposta simples a uma pergunta simples: "Ainda corresponde ao que eu confiava antes?"
Em Conclusão
Enquanto trabalhava neste projeto, a descoberta mais inesperada não foi o protocolo SSH em si. Fiquei surpreso com a atitude em relação ao known_hosts. Incluindo a minha. Acostumamo-nos a considerá-lo um cache temporário, que pode ser excluído sem remorso ao primeiro aviso do SSH. Mas, se olharmos mais de perto, é a única base local de identidades de servidores confiáveis utilizada pelo OpenSSH. E, assim que começamos a vê-lo dessa forma, torna-se surpreendente o quão poucas ferramentas existem ao seu redor. É por isso que o khm surgiu. Não como mais um cliente SSH, nem como substituto do OpenSSH. Mas como uma tentativa de tornar um pouco mais conveniente uma parte pequena, mas, a meu ver, injustamente esquecida, de seu modelo de confiança. O código-fonte do projeto, compilações binárias e documentação estão disponíveis no repositório: https://github.com/casablanque-code/khm
📤 Compartilhar & Baixar
🧰 Ferramentas recomendadas
Divulgação: alguns links são patrocinados. Podemos receber comissão se você comprar — sem custo extra para você. Só indicamos o que faz sentido para a comunidade.