Pular para o conteúdo

SSH por dentro: o protocolo, as chaves e o OpenSSH

Uma imersão no SSH: as três camadas do protocolo, a troca de chaves, a autenticação por chave e certificado, túneis, o OpenSSH e as falhas recentes.

Equipe Gothor· Publicado em · Atualizado em · 48 min de leitura
Capa do artigo SSH por dentro, com um cliente e um servidor ligados por um túnel cifrado em três camadas, a de transporte, a de autenticação e a de conexão, e os canais passando por dentro

Quase todo servidor Linux do mundo é administrado por SSH. É por ele que se entra no servidor para ver um log, que o código chega ao servidor numa implantação, que os arquivos de backup são copiados, que o repositório Git é clonado e que boa parte das ferramentas de automação conversa com as máquinas. É tão comum que ninguém pensa nele, até o dia em que um aviso diz que a "identificação do servidor mudou", ou que uma notícia anuncia uma falha que permite entrar num servidor sem senha.

O SSH resolve um problema que, em 1995, era muito concreto: os protocolos de acesso remoto da época mandavam tudo em texto aberto, inclusive as senhas. Ele cifra a conexão, autentica o servidor para o cliente e o cliente para o servidor, e permite passar outros tráfegos por dentro do mesmo túnel. Cada uma dessas coisas tem um mecanismo próprio, e entender os mecanismos é o que permite usar o SSH com segurança.

O artigo segue em quatro partes. A primeira conta de onde veio o SSH e como o protocolo é organizado, em três camadas. A segunda trata da criptografia: a troca de chaves, a chave do servidor e a autenticação do usuário, com chaves e certificados. A terceira cobre o uso: túneis, saltos, multiplexação e transferência de arquivos. A quarta trata do OpenSSH, das falhas recentes que mudaram a forma de configurar os servidores, das boas práticas e dos erros comuns.

De onde veio o SSH

Um ataque na universidade

O SSH foi criado por Tatu Ylönen, pesquisador da Universidade de Tecnologia de Helsinque, depois de um ataque de captura de senhas na rede da universidade, em 1995 [1]. A primeira versão, o SSH-1, foi pensada para substituir o rlogin, o telnet, o FTP e o rsh, que não tinham autenticação forte nem confidencialidade, e foi lançada como software gratuito em julho daquele ano [1]. Em dezembro de 1995, Ylönen fundou a SSH Communications Security, e as versões seguintes da empresa foram ficando cada vez mais proprietárias [1].

A adoção foi rápida. Segundo os números citados pela Wikipédia, a partir de um livro de 2001 sobre o assunto, o SSH tinha cerca de 20 mil usuários em cinquenta países no fim de 1995 e cerca de 2 milhões em 2000 [1].

O SSH-2 e o padrão

O SSH-1 tinha falhas de projeto, como uma de 1998 que permitia inserir conteúdo numa sessão, e hoje é considerado obsoleto [1]. A versão 2, incompatível com a primeira, foi padronizada pelo grupo de trabalho "secsh" do IETF, e os documentos centrais saíram em janeiro de 2006 [1]. A arquitetura está na RFC 4251, assinada por Tatu Ylönen e C. Lonvick [2].

O OpenSSH

Em 1999, desenvolvedores do OpenBSD criaram o OpenSSH a partir de uma bifurcação do OSSH, de Björn Grönvall, que por sua vez partia da última versão do SSH original publicada com licença aberta; o OpenSSH saiu com o OpenBSD 2.6 e é hoje a implementação mais usada [1]. É dele que vêm o cliente ssh, o servidor sshd e as ferramentas ssh-keygen, ssh-agent, scp e sftp que vêm em praticamente todo Linux, no macOS e no Windows.

Linha do tempo do SSH: em julho de 1995, o SSH-1 de Tatu Ylönen, depois de um ataque de captura de senhas em Helsinque; em 1999, o OpenSSH, criado por desenvolvedores do OpenBSD; em janeiro de 2006, o SSH-2 padronizado pelo IETF nas RFCs 4250 a 4256; em abril de 2022, o OpenSSH 9.0, com troca de chaves pós-quântica por padrão e o scp usando o protocolo SFTP; em dezembro de 2023, o ataque Terrapin e o OpenSSH 9.6; em julho de 2024, a falha regreSSHion e o OpenSSH 9.8; em abril de 2025, o OpenSSH 10.0, com o ML-KEM por padrão e o fim do DSA
Do ataque em Helsinque às chaves pós-quânticas: trinta anos de SSH

As três camadas do protocolo

A RFC 4251 organiza o SSH em três protocolos, cada um rodando sobre o anterior [2].

Diagrama das três camadas do SSH: embaixo, sobre o TCP, a camada de transporte, que autentica o servidor, cifra e protege a integridade, com a troca de chaves e a chave de host; no meio, a camada de autenticação do usuário, com chave pública, senha ou métodos interativos; em cima, a camada de conexão, que divide o túnel em canais, como a sessão de terminal, os comandos, o SFTP e os encaminhamentos de porta, cada um com controle de fluxo por janela
Transporte, autenticação e conexão: três protocolos, um em cima do outro

Transporte

O protocolo de transporte autentica o servidor, garante a confidencialidade e a integridade da conexão e, opcionalmente, a compressão; normalmente roda sobre uma conexão TCP/IP [2]. É ele que faz a troca de chaves e cifra tudo o que vem depois.

Autenticação do usuário

O protocolo de autenticação do usuário autentica o usuário do lado do cliente para o servidor, e roda sobre o de transporte [2]. Quando ele começa, a conexão já está cifrada: a senha ou a prova de posse da chave nunca passam em texto aberto.

Conexão

O protocolo de conexão divide o túnel cifrado em vários canais lógicos, e roda sobre o de autenticação [2]. É ele que permite, numa mesma conexão, ter uma sessão de terminal, executar comandos, transferir arquivos e encaminhar portas.

A separação em camadas tem uma vantagem prática: cada problema tem o seu lugar. Um aviso de chave de host trocada é da camada de transporte; uma falha de login, da de autenticação; um túnel que não abre, da de conexão.

O transporte por dentro

A troca de versões

A conexão começa com uma troca de texto simples: cada lado envia uma linha de identificação no formato SSH-versãodoprotocolo-versãodosoftware, seguida de comentários opcionais [3]. A versão do protocolo precisa ser 2.0, e a linha tem no máximo 255 caracteres [3]. Numa conexão real, a linha do servidor costuma dizer algo como SSH-2.0-OpenSSH_10.0, e é por isso que ferramentas de varredura conseguem descobrir a versão do OpenSSH de um servidor sem se autenticar.

Os pacotes

Depois disso, tudo viaja em pacotes binários com campos fixos: o tamanho do pacote, o tamanho do preenchimento, a carga, que pode vir comprimida, pelo menos quatro bytes de preenchimento aleatório e o código de autenticação da mensagem, que fica vazio até um algoritmo de autenticação ser combinado [3]. O preenchimento completa o pacote até um múltiplo do tamanho de bloco da cifra ou de 8 bytes, o que for maior [3], para esconder o tamanho exato do que é enviado.

A troca de chaves

O passo central do transporte é a troca de chaves. Os dois lados combinam os algoritmos e, por meio de uma troca baseada em criptografia de chave pública, chegam a um segredo compartilhado, K, e a um resumo da troca, H, sem que ninguém que observe a rede consiga calcular o segredo [3]. O resumo H da primeira troca serve também como identificador da sessão, e esse identificador não muda mesmo que as chaves sejam renegociadas depois [3]. Das chaves derivadas do segredo saem as chaves da cifra e da autenticação das mensagens.

A troca de chaves de novo

A RFC recomenda trocar as chaves depois de cada gigabyte de dados transmitidos ou de cada hora de conexão, o que vier primeiro, com a ressalva de que a troca é uma operação de chave pública e não deve acontecer com frequência demais [3]. Em conexões longas, como um túnel que fica aberto o dia inteiro, isso limita a quantidade de dados protegidos pela mesma chave.

O pedido de serviço

Terminada a troca de chaves, o cliente pede um serviço pelo nome, como ssh-userauth, e o servidor aceita ou encerra a conexão [3]. É aí que começa a camada de autenticação do usuário.

A troca de chaves pós-quântica

A troca de chaves do SSH mudou muito nos últimos anos, por causa de uma ameaça que ainda não existe na prática: o computador quântico.

Guardar agora, decifrar depois

A privacidade de uma sessão SSH depende da troca de chaves. A página do OpenSSH sobre o assunto explica o risco: um atacante pode gravar hoje sessões cifradas e decifrá-las no futuro, quando houver um computador quântico capaz de quebrar a criptografia de chave pública atual; é o ataque conhecido como "guardar agora, decifrar depois" [4]. Para dados que precisam continuar secretos por anos, como as credenciais e as configurações que passam por uma sessão de administração, o risco é real hoje.

Os algoritmos híbridos

Por isso, o OpenSSH usa troca de chaves pós-quântica por padrão desde a versão 9.0, de abril de 2022, quando o algoritmo sntrup761x25519-sha512 passou a ser o padrão [4][5]. O algoritmo mlkem768x25519-sha256, baseado no ML-KEM, foi acrescentado na versão 9.9 e virou o padrão na versão 10.0, de abril de 2025 [4][5].

Os dois são híbridos: combinam um algoritmo pós-quântico com o X25519, a troca de chaves clássica por curvas elípticas [4]. A vantagem é de segurança: se a parte pós-quântica for quebrada no futuro, a combinação não fica mais fraca do que a troca clássica sozinha [4].

O aviso da versão 10.1

A partir da versão 10.1, o cliente passa a registrar um aviso quando uma conexão usa uma troca de chaves que não é pós-quântica, controlado pela nova opção WarnWeakCrypto, ligada por padrão [4][5]. A recomendação para quem vê o aviso é atualizar o servidor ou conferir se a opção KexAlgorithms não está desligando os algoritmos pós-quânticos [4].

E as assinaturas

As assinaturas RSA e ECDSA também são vulneráveis aos computadores quânticos, mas o OpenSSH considera o problema menos urgente: forjar uma assinatura no futuro não expõe o tráfego passado, como acontece com a quebra da troca de chaves [4]. A prioridade é aposentar as chaves de assinatura clássicas antes de os computadores quânticos se tornarem práticos, e o OpenSSH planeja acrescentar assinaturas pós-quânticas [4]. A página de manual do ssh-keygen já lista um tipo híbrido, o mldsa44-ed25519, entre os tipos de chave aceitos [6].

A chave do servidor

A troca de chaves garante que ninguém no meio da rede leia a conversa. Mas não garante, sozinha, que o cliente está falando com o servidor certo. Quem garante isso é a chave de host.

Por que ela existe

Cada servidor deve ter uma chave de host, e o cliente a usa durante a troca de chaves para conferir que está falando com o servidor correto; para isso, precisa conhecer de antemão a chave pública do servidor [2]. Sem essa conferência, um atacante no meio do caminho poderia fazer uma troca de chaves com o cliente fingindo ser o servidor, e outra com o servidor fingindo ser o cliente, lendo tudo no meio.

Os dois modelos de confiança

A RFC 4251 descreve duas formas de o cliente conhecer a chave do servidor [2]:

  • uma base local: o cliente guarda um mapa do nome de cada servidor para a sua chave pública; não precisa de infraestrutura central, mas a base pode ficar trabalhosa de manter;
  • uma autoridade certificadora: uma autoridade confiável certifica a associação entre o nome e a chave, e o cliente só precisa conhecer a chave da autoridade; em troca, toda chave de host precisa ser certificada antes do uso.

No OpenSSH, a base local é o arquivo ~/.ssh/known_hosts, e o sistema também consulta o /etc/ssh/ssh_known_hosts [7].

A primeira conexão

O protocolo permite pular a conferência na primeira conexão, e é o que acontece quando o cliente pergunta "a autenticidade do servidor não pode ser estabelecida; deseja continuar?". A RFC é clara sobre o custo: a conexão continua protegida contra quem só escuta a rede, mas fica vulnerável a um ataque ativo de intermediário [2]. Responder "sim" sem conferir a impressão digital é confiar no primeiro que atender. A conferência pode ser feita comparando a impressão digital mostrada com a que o administrador do servidor obteve no próprio servidor, por um canal confiável, como o console da máquina ou o painel do provedor.

Quando a chave muda

Se a chave de um servidor conhecido muda, o cliente avisa e desliga a autenticação por senha, para não entregar a senha a um impostor [7]. A opção StrictHostKeyChecking controla como o cliente trata as chaves desconhecidas e as trocadas [7]. Uma chave que mudou pode ter explicação, como a reinstalação do servidor, mas o aviso deve ser conferido, e não contornado apagando a linha do known_hosts por reflexo.

Há duas formas de tornar a conferência menos manual. A primeira é publicar as impressões digitais no DNS, em registros SSHFP, que o cliente compara com a chave apresentada, controlado pela opção VerifyHostKeyDNS [7]. O tipo de registro foi definido pela RFC 4255, de janeiro de 2006, com o código 44, e traz o algoritmo da chave, o tipo de resumo e a impressão digital [22]. A RFC tem uma condição que não pode ser esquecida: uma chave conferida assim não deve ser considerada confiável se o registro não tiver sido autenticado pelo DNSSEC [22]. Sem o DNSSEC, um atacante que falsifique a resposta do DNS falsifica também a impressão digital, e a conferência não vale nada.

A segunda forma é o modelo de autoridade certificadora, com os certificados de host, tratados adiante.

Diagrama da conferência da chave de host: na troca de chaves, o servidor assina o resumo da troca com a chave de host; o cliente procura a chave no known_hosts; se a chave é conhecida e confere, segue; se é desconhecida, pergunta e, sem conferência, fica exposto a um intermediário na primeira conexão; se mudou, avisa e desliga a autenticação por senha; as alternativas à conferência manual são os registros SSHFP no DNS e os certificados de host assinados por uma autoridade
A chave de host: o que impede o cliente de falar com um impostor

A autenticação do usuário

Com a conexão cifrada e o servidor conferido, falta o servidor saber quem é o usuário.

Os métodos

A RFC 4252 define os métodos de autenticação [8]: a chave pública, obrigatória em toda implementação; a senha e a autenticação baseada no host, opcionais; e o método none, reservado, que não pode ser anunciado e serve principalmente para o cliente perguntar quais métodos o servidor aceita. É o servidor que conduz: ele diz ao cliente quais métodos podem continuar, e o cliente os tenta em qualquer ordem [8].

Vários passos

O protocolo permite exigir mais de um método. Uma resposta de falha traz um indicador de "sucesso parcial", verdadeiro quando o passo deu certo mas ainda faltam outros, e a mensagem de sucesso só é enviada quando todos os passos terminam [8]. É o que permite exigir, por exemplo, uma chave e um código de uso único.

Os limites

A RFC recomenda que o servidor desconecte depois de um tempo sem autenticação, de 10 minutos, e limite as tentativas falhas, com um limite recomendado de 20 por sessão [8]. O OpenSSH é mais rígido: o tempo padrão para autenticar, o LoginGraceTime, é de 120 segundos [9].

Como a chave pública prova a identidade

Na autenticação por chave pública, o cliente assina um bloco de dados que começa com o identificador da sessão e inclui os detalhes do pedido; como o servidor consegue conferir essa assinatura com a chave pública, a assinatura prova que o cliente tem a chave privada correspondente [8]. Dois detalhes tornam o método forte. A chave privada nunca sai do cliente: o servidor recebe só uma assinatura. E a assinatura inclui o identificador da sessão, que é diferente a cada conexão, por isso não pode ser reaproveitada numa sessão de um atacante.

Comparada à senha, a vantagem é grande. Uma senha fraca pode ser adivinhada; uma senha reaproveitada pode vazar de outro serviço; uma senha digitada num servidor falso é entregue ao falsário. Nada disso acontece com uma chave.

As chaves do usuário

Os tipos

O ssh-keygen gera chaves de vários tipos: ecdsa, ecdsa-sk, ed25519, que é o padrão, ed25519-sk, rsa e o híbrido mldsa44-ed25519 [6]. O DSA foi removido do OpenSSH na versão 10.0, completando uma descontinuação que começou em 2015 [5].

As recomendações da Mozilla para o OpenSSH resumem a escolha: usar o Ed25519 quando não houver necessidade de compatibilidade com sistemas muito antigos, e, para compatibilidade, uma chave RSA de 4.096 bits [10].

# uma chave Ed25519, com um comentário que identifica o dono
ssh-keygen -t ed25519 -C "maria@notebook"

Por que o Ed25519

O algoritmo ssh-ed25519 foi padronizado para o SSH pela RFC 8709, de fevereiro de 2020, que trouxe também o ssh-ed448; os dois servem só para assinatura, e os procedimentos vêm da especificação do EdDSA, a RFC 8032 [21]. No Ed25519, a chave pública tem 32 bytes, e a assinatura, 64 [21].

O tamanho pequeno tem efeitos práticos. Uma linha do authorized_keys com uma chave Ed25519 cabe numa linha de terminal, enquanto uma RSA de 4.096 bits ocupa várias; a geração da chave e as assinaturas são rápidas; e não há um tamanho de chave a escolher, e portanto não há como escolher um fraco demais. Somados à recomendação da Mozilla e ao fato de ser o tipo padrão do ssh-keygen atual, esses motivos explicam por que o Ed25519 virou a escolha de referência [6][10].

Chaves em dispositivos físicos

Os tipos terminados em -sk usam um autenticador físico, como uma chave de segurança FIDO: o arquivo da chave privada guarda só um identificador, e a chave privada de verdade fica no dispositivo, sem poder ser exportada [6]. Opcionalmente, a chave pode ficar residente no próprio dispositivo, o que facilita o uso em vários computadores, mas aumenta o risco se ele for roubado [6]. Com uma chave -sk, quem rouba o arquivo do notebook não consegue usá-lo sem o dispositivo.

A frase-senha e a separação de chaves

A Mozilla recomenda proteger as chaves de usuário com uma frase-senha forte e nunca copiá-las para fora da própria estação de trabalho ou dos dispositivos pessoais [10]. As chaves de máquinas, usadas por automações, podem não ter frase-senha, mas devem ser únicas para cada uso, restritas aos privilégios mínimos e registradas num inventário; e devem evitar contas de root ou com sudo [10]. Também recomenda usar chaves separadas para propósitos diferentes [10].

O ssh-agent

Digitar a frase-senha a cada conexão cansa, e é para isso que existe o ssh-agent: ele guarda as chaves privadas usadas na autenticação por chave pública, e o ssh recorre a ele ao entrar em outras máquinas [18]. As chaves são acrescentadas com o ssh-add, ou pelo próprio ssh, quando a opção AddKeysToAgent está ligada [18]. A página de manual destaca que as frases-senha e as chaves privadas nunca passam pela rede: o agente faz a assinatura e devolve só o resultado [18].

O cliente encontra o agente pela variável de ambiente SSH_AUTH_SOCK, que aponta para um socket criado quando o agente inicia [18]. O manual é honesto sobre o limite dessa proteção: o socket só é acessível ao próprio usuário, mas pode ser usado facilmente pelo root ou por outra instância do mesmo usuário [18]. Por padrão, as chaves ficam no agente para sempre; a opção -t define um tempo máximo de vida, e o ssh-add pode definir um tempo para cada chave [18]. A Mozilla sugere, quando for preciso usar o agente de forma mais cuidadosa, pedir confirmação a cada uso, com ssh-add -c, ou dar um prazo de validade às chaves carregadas, com ssh-add -t [10].

Um agente com as chaves carregadas o dia inteiro, numa máquina compartilhada, é um agente que qualquer processo com privilégio suficiente pode usar. Um prazo de algumas horas, que obriga a digitar a frase-senha de novo depois do almoço, é um custo pequeno para um risco grande.

Certificados SSH

Com dezenas de servidores e de pessoas, manter os arquivos authorized_keys e known_hosts em todo lugar vira um trabalho em si. Os certificados do OpenSSH resolvem isso com uma autoridade certificadora, o segundo modelo de confiança da RFC 4251 [2].

Como funcionam

O ssh-keygen assina uma chave pública com a chave de uma autoridade e produz um certificado de usuário ou de host [6]. O certificado reúne a chave pública, uma identidade, um ou mais principais, que são os nomes de usuário ou de servidor para os quais ele vale, e opções [6]. As opções principais do comando são [6]:

  • -s: a chave da autoridade usada para assinar;
  • -I: a identidade da chave, usada nos registros e na revogação;
  • -n: os principais;
  • -h: gera um certificado de host, em vez de usuário;
  • -V: o intervalo de validade.
# um certificado de usuário, válido por 8 horas, para o usuário "deploy"
ssh-keygen -s ca_usuarios -I maria-2026-11-16 -n deploy -V +8h id_ed25519.pub

O que muda

Com certificados de usuário, o servidor não precisa de uma lista das chaves de cada pessoa: basta confiar na chave da autoridade. Com certificados de host, o cliente não precisa conferir a impressão digital de cada servidor: basta confiar na autoridade dos hosts, e o aviso da primeira conexão desaparece. E a validade curta muda a gestão de acessos: um certificado de oito horas, emitido a cada manhã, expira sozinho, e quem sai da equipe deixa de ter acesso sem que ninguém precise caçar a chave dele em cada servidor.

Revogação

Para os casos em que é preciso cancelar uma chave ou um certificado antes da hora, o OpenSSH tem as listas de revogação de chaves, os arquivos KRL, um formato binário compacto que lista as chaves e os certificados revogados, criados e consultados pelo próprio ssh-keygen [6].

Restringir o que cada chave pode fazer

Uma linha do authorized_keys não precisa dar acesso total. O formato aceita opções antes da chave, que restringem o que pode ser feito com ela [16].

As opções

As opções mais úteis são estas [16]:

  • **command="...":** força um comando a cada uso da chave, ignorando o que o cliente pediu; o comando original fica na variável SSH_ORIGINAL_COMMAND;
  • **from="...":** exige, além da chave, que a origem case com uma lista de nomes ou endereços, inclusive em notação CIDR;
  • **no-port-forwarding**, **no-agent-forwarding**, **no-pty** e **no-X11-forwarding:** bloqueiam, cada uma, um recurso;
  • **permitopen="host:porta":** limita os encaminhamentos locais a um destino específico;
  • **restrict:** liga todas as restrições de uma vez, desligando os encaminhamentos de porta, de agente e de X11, a alocação de terminal e o ~/.ssh/rc, e inclui automaticamente as restrições que vierem a existir no futuro;
  • **expiry-time="...":** recusa a chave depois de uma data;
  • **cert-authority:** marca a chave como uma autoridade certificadora, cujos certificados de usuário são aceitos, e o principals= limita os principais aceitos.

Uma chave de backup

O exemplo da página de manual mostra o uso típico: uma chave que só pode rodar um comando de backup, sem terminal nem encaminhamentos [16].

restrict,command="/usr/local/bin/backup-diario",from="10.0.5.20" ssh-ed25519 AAAA... backup@servidor-backup

Com essa linha, mesmo que a chave do servidor de backup vaze, quem a tiver só consegue rodar o backup, e só a partir do endereço do servidor de backup. Sem as opções, a mesma chave daria um terminal completo.

O restrict tem uma vantagem sobre a lista de proibições individuais: como ele inclui as restrições futuras, uma versão nova do OpenSSH que acrescente um recurso não abre uma porta nova numa chave restrita [16]. E é possível reabrir um recurso específico, como o terminal, com restrict,pty [16].

Chaves que expiram

O expiry-time dá às chaves comuns uma validade, como os certificados têm [16]. Para um acesso temporário, como o de um fornecedor durante uma implantação, uma chave com data de expiração evita a situação comum em que o acesso continua lá anos depois de o projeto terminar.

Quando uma chave vaza

Mais cedo ou mais tarde, uma chave vaza: um notebook roubado, um arquivo copiado para o lugar errado, uma chave de automação esquecida num repositório. O que fazer depende de como os acessos foram montados.

**Com chaves soltas no authorized_keys**, é preciso remover a linha da chave em cada servidor onde ela foi cadastrada. É aqui que a falta de inventário cobra o preço: sem saber onde a chave estava, não há como ter certeza de que ela foi removida de todo lugar. Um LogLevel VERBOSE, que registra a impressão digital da chave em cada login, ajuda a descobrir onde ela foi usada [10].

Com certificados, o caminho é a revogação: a chave ou o certificado entra na lista de revogação do OpenSSH, o arquivo KRL, gerado e consultado pelo ssh-keygen, e os servidores passam a recusá-lo [6]. Se os certificados forem de validade curta, como algumas horas, o problema se resolve sozinho no fim do prazo, mesmo antes da revogação chegar a todos os servidores.

Para chaves de host, a marcação @revoked no known_hosts faz o cliente nunca mais aceitar aquela chave e avisar quando a encontrar [16].

Em todos os casos, a chave vazada é substituída por uma nova, e o incidente deixa uma lição para o desenho: quanto menos tempo uma credencial vale e quanto menos ela pode fazer, menor é o estrago quando ela vaza. É o argumento mais forte a favor dos certificados de validade curta e das restrições no authorized_keys.

Certificados no servidor e no cliente

A seção sobre certificados mostrou como emiti-los. Para que funcionem, os dois lados precisam confiar nas autoridades.

No servidor

Do lado do servidor, a opção TrustedUserCAKeys lista as chaves das autoridades que assinam certificados de usuário; os certificados só são aceitos se trouxerem uma lista de principais, e os sem principais são recusados [15]. Para que o servidor apresente o próprio certificado de host, a opção HostCertificate aponta o arquivo do certificado, cuja chave pública precisa casar com uma das HostKey configuradas [15].

# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/ca_usuarios.pub
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

No cliente

Do lado do cliente, o known_hosts aceita a marcação @cert-authority antes de uma chave, que a torna uma autoridade: qualquer servidor do domínio indicado que apresente um certificado assinado por ela é aceito [16]. E a marcação @revoked marca uma chave como revogada: ela nunca é aceita, e o cliente avisa quando a encontra [16].

# ~/.ssh/known_hosts
@cert-authority *.exemplo.com.br ssh-ed25519 AAAA... ca-hosts

Com essa única linha, o cliente confia em todos os servidores do domínio que tiverem certificado de host, sem perguntar nada na primeira conexão, e sem que alguém precise distribuir as impressões digitais de cada servidor.

Contas só para transferir arquivos

Um caso muito comum é dar a um parceiro acesso para enviar ou buscar arquivos, como arquivos de retorno bancário, notas fiscais ou relatórios, sem dar a ele um terminal no servidor. O OpenSSH resolve isso com duas opções [15].

ForceCommand e ChrootDirectory

O ForceCommand roda um comando fixo no lugar do que o cliente pedir, e o valor internal-sftp força um servidor SFTP embutido no próprio sshd [15]. O ChrootDirectory confina a sessão numa pasta depois da autenticação: o usuário passa a ver aquela pasta como a raiz do sistema [15]. A página de manual impõe uma condição: todos os componentes do caminho precisam pertencer ao root e não podem ter permissão de escrita para o grupo nem para os outros [15].

# /etc/ssh/sshd_config
Match Group parceiros-sftp
    ChrootDirectory /srv/sftp/%u
    ForceCommand internal-sftp
    DisableForwarding yes
    PasswordAuthentication no

O detalhe dos encaminhamentos

O exemplo tem uma linha que costuma ser esquecida. A página de manual avisa que o ForceCommand não restringe os encaminhamentos de porta, que precisam do DisableForwarding ou das opções específicas de cada recurso [15]. Sem ela, um parceiro com acesso só de SFTP ainda poderia usar o servidor como ponte para a rede interna, com um encaminhamento local.

Como o usuário não consegue gravar na raiz confinada, que pertence ao root, a prática é criar dentro dela uma pasta de trabalho que pertença a ele, como /srv/sftp/parceiro/entrada.

RSA, SHA-1 e as assinaturas

Muitos servidores antigos ainda usam chaves RSA, e uma mudança de 2018 explica avisos e falhas de conexão que aparecem em sistemas velhos.

O problema do ssh-rsa

O algoritmo de assinatura original das chaves RSA no SSH, o ssh-rsa, usa o SHA-1. A RFC 8332, de março de 2018, registra que os algoritmos originais "são hoje considerados deficientes" e que o NIST não permite mais o SHA-1 para gerar assinaturas digitais, fora de orientações específicas [17]. Ela definiu dois algoritmos novos para as chaves RSA: o rsa-sha2-256, recomendado, e o rsa-sha2-512, opcional, ambos com resumos SHA-2 [17].

A mesma chave, outra assinatura

A parte importante da RFC é que a chave não muda: as chaves RSA existentes funcionam com os algoritmos novos sem nova codificação, mantêm o formato ssh-rsa de chave pública, e as impressões digitais já confiadas continuam valendo [17]. O que muda é o algoritmo da assinatura.

Isso explica uma confusão comum. Quando um cliente novo se recusa a conectar num servidor muito antigo com uma chave RSA, o problema raramente é a chave: é o servidor antigo, que só sabe assinar com SHA-1. A solução certa é atualizar o servidor; a solução de emergência, reativar o ssh-rsa só para aquele servidor no ~/.ssh/config, deve ser tratada como temporária. E, para chaves novas, a recomendação continua sendo o Ed25519 [6][10].

A conexão: canais e túneis

A camada de conexão é a que dá ao SSH a versatilidade que vai muito além do terminal remoto.

Os canais

O protocolo de conexão oferece sessões interativas, execução remota de comandos, encaminhamento de conexões TCP/IP e de X11, tudo multiplexado num único túnel cifrado [11]. Tudo isso são canais: qualquer um dos lados pode abrir um canal, vários canais dividem a mesma conexão, e cada canal tem controle de fluxo por uma janela, que limita quantos bytes podem ser enviados até o outro lado liberar mais [11].

Os tipos de canal são quatro [11]:

  • session: roda um terminal, um comando ou um subsistema, como o SFTP;
  • x11: leva conexões gráficas encaminhadas;
  • forwarded-tcpip: aberto pelo servidor quando chega uma conexão numa porta encaminhada remotamente;
  • direct-tcpip: pede ao outro lado que se conecte a um endereço e porta, para um encaminhamento local.

A RFC tem regras de segurança para eles: os clientes devem recusar pedidos de abertura de sessão vindos do servidor, e as implementações devem recusar os canais de X11 e de portas que não foram pedidos [11].

Os encaminhamentos de porta

O cliente ssh tem três formas de encaminhar portas [7]:

  • **local, com -L:** as conexões a uma porta do computador local são levadas pelo túnel e saem do lado do servidor;
  • **remoto, com -R:** o inverso, uma porta no servidor é encaminhada de volta para o cliente; por padrão, ela só escuta no endereço local do servidor, a menos que o GatewayPorts permita outra coisa;
  • **dinâmico, com -D:** o ssh vira um proxy SOCKS, nas versões 4 e 5 do protocolo.
# acessar, do notebook, um banco que só escuta dentro da rede do servidor
ssh -L 5433:localhost:5432 usuario@servidor
# agora, psql -h localhost -p 5433 chega ao banco pelo túnel

O encaminhamento local é a forma mais segura de acessar um serviço interno, como um banco de dados, sem expô-lo à internet: o banco continua escutando só na rede interna, e o acesso passa pelo SSH, com toda a autenticação dele.

Diagrama dos três encaminhamentos de porta do SSH: no local, com -L, uma porta do notebook leva, pelo túnel, a um serviço que só o servidor alcança, como um banco de dados interno; no remoto, com -R, uma porta do servidor leva de volta a um serviço do notebook; no dinâmico, com -D, o ssh vira um proxy SOCKS, e o navegador do notebook acessa a rede do servidor; e o salto, com -J, em que o cliente conecta primeiro a um servidor intermediário e de lá ao destino, sem levar as chaves para o intermediário
Encaminhamento local, remoto, dinâmico e o salto: o túnel serve para muito mais que o terminal

Saltar por um servidor intermediário

Muitas redes só permitem chegar aos servidores internos passando por um servidor de entrada, o bastião. A opção -J, o ProxyJump, faz isso de uma vez: o ssh conecta primeiro ao servidor intermediário e, de lá, encaminha uma conexão TCP até o destino [7].

ssh -J bastiao.exemplo.com.br usuario@servidor-interno

A grande vantagem sobre a alternativa antiga, entrar no bastião e de lá conectar ao destino, é que as chaves continuam só no notebook: a autenticação no servidor interno é feita de ponta a ponta, pelo túnel.

O risco do encaminhamento do agente

A alternativa antiga costumava vir com o encaminhamento do agente, a opção -A, para que as chaves do notebook pudessem ser usadas a partir do bastião. A página de manual do ssh alerta: usuários capazes de contornar as permissões de arquivos no servidor remoto conseguem acessar o agente local pela conexão encaminhada; não conseguem extrair as chaves, mas conseguem usá-las para se autenticar onde quiserem enquanto o encaminhamento estiver ativo [7]. A própria página sugere o servidor de salto como opção mais segura [7], e a Mozilla recomenda não manter o encaminhamento do agente quando ele não for necessário e preferir o ProxyJump [10].

Usar o SSH no dia a dia

O arquivo de configuração

O arquivo ~/.ssh/config evita digitar as mesmas opções a cada conexão. Os blocos Host e Match limitam as opções seguintes aos servidores ou condições que casam com eles, e, como vale o primeiro valor encontrado, as configurações específicas devem vir no começo do arquivo e os padrões gerais no fim [12].

Host interno-*
    ProxyJump bastiao.exemplo.com.br
    User deploy
    IdentityFile ~/.ssh/id_ed25519_deploy
    IdentitiesOnly yes

Host *
    ServerAliveInterval 60
    HashKnownHosts yes

Algumas opções resolvem problemas conhecidos. O IdentitiesOnly faz o cliente usar só as chaves configuradas, mesmo que o agente ofereça outras, o que evita ser recusado por excesso de tentativas quando o agente guarda muitas chaves [12]. O ServerAliveInterval manda mensagens de manutenção cifradas quando a conexão fica sem dados, evitando que um firewall derrube uma sessão parada [12]. E o HashKnownHosts, recomendado pela Mozilla, guarda os nomes dos servidores do known_hosts em forma de resumo, para esconder a quais servidores as suas chaves dão acesso se o arquivo vazar [10].

Reaproveitar a conexão

A troca de chaves e a autenticação levam algum tempo, e, para quem abre muitas sessões seguidas no mesmo servidor, como um script de implantação, esse tempo se soma. O ControlMaster permite compartilhar várias sessões numa mesma conexão de rede; o ControlPath define onde fica o socket de controle, que deve ficar numa pasta em que outros usuários não possam gravar; e o ControlPersist mantém a conexão principal aberta em segundo plano depois que a primeira sessão termina [12].

O ritmo da digitação

Uma sessão interativa tem um vazamento discreto: cada tecla digitada gera um pacote, e o intervalo entre os pacotes, visível para quem observa a rede mesmo com tudo cifrado, revela o ritmo da digitação, o que em tese ajuda a adivinhar o que foi digitado. A versão 9.5 do OpenSSH acrescentou ao cliente uma ofuscação do tempo das teclas, controlada pela opção ObscureKeystrokeTiming [5]. Ela é, aliás, uma das proteções que o ataque Terrapin conseguia desligar, como mostra a seção sobre as falhas recentes [13].

Sair de uma sessão travada

Uma sessão que trava por uma queda de rede pode ser encerrada com a sequência de escape ~., digitada no começo de uma linha [7]. É um detalhe que poupa muitos terminais fechados à força.

A transferência de arquivos

O SSH tem dois jeitos clássicos de copiar arquivos: o scp, herdeiro do antigo rcp, e o sftp, que roda como um subsistema dentro de uma sessão. Na versão 9.0, o OpenSSH mudou o scp para usar o protocolo SFTP por padrão, em vez do protocolo legado scp/rcp [5]. O comando continua o mesmo para quem usa, mas por baixo deixa de depender de um protocolo antigo, que tinha problemas conhecidos de interpretação de nomes de arquivo.

O SSH no Git e no GitHub

Para muita gente, o primeiro contato com chaves SSH não é um servidor, e sim um repositório Git. O mecanismo é o mesmo: o serviço de hospedagem guarda a chave pública, e o cliente prova a posse da chave privada.

Autenticar no repositório

Ao clonar um repositório pelo endereço SSH, como git@github.com:empresa/projeto.git, o Git chama o ssh, que se autentica com a chave da pessoa e pede ao servidor o acesso ao repositório. Tudo o que vale para os servidores vale aqui: a conferência da chave de host na primeira conexão, as chaves Ed25519, a frase-senha e o agente.

Assinar commits com a mesma chave

A mesma chave pode servir para outra coisa: assinar os commits. O GitHub aceita commits e tags assinados com chaves SSH, além do GPG e do S/MIME, e marca como "Verified" os que têm uma assinatura verificável [19]. Para isso, a chave precisa ser cadastrada na conta como chave de assinatura, e, se ela já for usada para autenticação, pode ser cadastrada de novo para assinatura [19]. A verificação de assinaturas SSH existe a partir do Git 2.34 [19].

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

A assinatura responde a uma pergunta que a autenticação não responde: quem escreveu aquele commit. Qualquer pessoa pode configurar o Git com o nome e o e-mail de outra; só quem tem a chave privada consegue produzir a assinatura. Em repositórios com regras de proteção que exigem commits assinados, isso fecha uma porta de falsificação.

O SSH no Windows

O SSH deixou de ser uma ferramenta só do mundo Unix. O Windows traz o OpenSSH como um recurso opcional, mantido e atualizado pela Microsoft pelo Windows Update, a partir do Windows 10, versão 1809, e do Windows Server 2019 [20]. A partir do Windows Server 2025, o OpenSSH vem instalado por padrão, e o serviço sshd pode ser ligado ou desligado pelo Gerenciador do Servidor [20].

Nas versões anteriores, a instalação é feita pelos recursos opcionais ou pelo PowerShell, com o Add-WindowsCapability, e a instalação do servidor cria uma regra de firewall que libera a porta 22 [20]. Para limitar quem pode entrar por SSH no Windows Server 2025, a documentação indica o grupo de usuários "OpenSSH Users" [20]. A Microsoft mantém também uma bifurcação própria do projeto, cujas versões podem diferir da que vem no Windows [20].

Para quem administra um ambiente misto, isso significa que o mesmo cliente, as mesmas chaves e os mesmos certificados podem servir para os servidores Linux e Windows, o que simplifica bastante a gestão de acessos.

O servidor: o OpenSSH na prática

A configuração de referência

As recomendações da Mozilla para o servidor, no perfil "moderno", são uma boa referência [10]:

  • chaves de host, com o Ed25519 primeiro na lista;
  • algoritmos, com o curve25519 na troca de chaves e o ChaCha20-Poly1305 ou o AES-GCM na cifra, preferindo as variantes que cifram antes de autenticar;
  • **AuthenticationMethods publickey**, para aceitar só login por chave;
  • **PermitRootLogin no**, para que os administradores entrem com usuários comuns e usem sudo, deixando cada ação atribuível a uma pessoa;
  • **LogLevel VERBOSE**, que registra a impressão digital da chave usada em cada login, criando uma trilha de auditoria.
# trecho de /etc/ssh/sshd_config
HostKey /etc/ssh/ssh_host_ed25519_key
AuthenticationMethods publickey
PermitRootLogin no
LogLevel VERBOSE

Senhas e segundo fator

O segundo fator de autenticação é recomendado onde for possível, mas tem uma armadilha que a Mozilla destaca: os códigos de uso único exigem a autenticação interativa por teclado, que pode permitir login por senha, e por isso o PasswordAuthentication no não basta; é preciso configurar também o PAM para bloquear a autenticação por senha do sshd [10]. As contas de máquina não conseguem completar o segundo fator, e a recomendação é permitir que elas entrem só a partir de faixas de endereços privados, protegidas por outra camada, como uma VPN, num bloco Match User que exija chave pública [10].

O rodízio das chaves de host

Trocar a chave de host de um servidor costuma ser adiado por um motivo prático: todos os clientes vão ver o aviso de chave trocada. O OpenSSH tem um mecanismo para suavizar isso. Depois da autenticação, o servidor pode anunciar chaves de host adicionais, e a opção UpdateHostKeys do cliente controla se ele as aceita e as acrescenta ao known_hosts, com os valores sim, não ou perguntar [12]. Com isso, um servidor pode passar a anunciar uma chave Ed25519 nova ao lado da antiga, os clientes vão aprendendo a nova nas conexões normais, e a antiga pode ser retirada depois, sem uma onda de avisos.

A Mozilla lembra o outro lado da questão: quando um servidor passa a oferecer um tipo de chave diferente do que está no known_hosts, como uma Ed25519 onde havia uma RSA, o cliente pode mostrar o aviso de chave trocada, e a chave nova deve ser conferida antes da conexão [10].

A porta 22

O SSH escuta, por padrão, na porta 22, e é ela que a instalação do servidor no Windows libera no firewall [20]. Um servidor com a porta 22 aberta para a internet recebe tentativas automáticas de login o tempo todo, e as defesas que vimos, como o PerSourcePenalties, o MaxStartups e a autenticação só por chave, existem justamente para isso [15].

Mudar a porta para outro número reduz o ruído nos registros, porque muitos robôs só tentam a 22, mas não acrescenta segurança de verdade: uma varredura encontra a porta nova em segundos. As medidas que protegem são as outras: aceitar só chaves, limitar quem pode entrar, manter o OpenSSH atualizado e, quando possível, não expor o SSH à internet, deixando-o acessível só por uma VPN ou por um servidor de salto.

Penalidades por origem

A versão 9.8 do OpenSSH acrescentou a opção PerSourcePenalties, ligada por padrão [5]. Ela penaliza as origens que derrubam o sshd, que falham na autenticação, que se desconectam sem tentar autenticar ou que estouram o LoginGraceTime, e recusa conexões daquela faixa de endereços por um tempo [15]. As penalidades padrão vão de 1 segundo, para quem se desconecta sem tentar, a 90 segundos, para quem derruba o servidor; uma falha de autenticação custa 5 segundos [15]. É uma defesa contra as tentativas automáticas de adivinhar senhas, que qualquer servidor SSH exposto na internet recebe o dia inteiro.

Os padrões que vale conhecer

A página de manual do sshd_config traz os valores padrão de cada opção, e alguns surpreendem [15]:

  • **PermitRootLogin** tem o padrão prohibit-password: o root pode entrar por chave, mas não por senha nem por autenticação interativa;
  • **PasswordAuthentication** e **KbdInteractiveAuthentication** vêm ligadas por padrão, ou seja, um servidor recém-instalado aceita senhas, a menos que a distribuição mude isso;
  • **MaxAuthTries** permite 6 tentativas de autenticação por conexão, e as falhas passam a ser registradas quando chegam à metade disso;
  • **MaxStartups**, com o padrão 10:30:100, controla as conexões que ainda não se autenticaram: acima de 10, as novas passam a ser recusadas ao acaso, com probabilidade crescente, e acima de 100, todas;
  • **LoginGraceTime** dá 120 segundos para o cliente se autenticar;
  • **ClientAliveInterval** vem em zero, sem mensagens de manutenção; com o ClientAliveCountMax, de 3 por padrão, um intervalo de 15 segundos derruba os clientes sem resposta em cerca de 45 segundos.

As opções AllowUsers e AllowGroups não têm restrição por padrão; quando definidas, só os usuários ou grupos da lista podem entrar, e as regras de negação têm precedência sobre elas [15]. Num servidor com muitos usuários de sistema, uma lista curta de quem pode entrar por SSH é uma das proteções mais simples e eficazes.

SSH em automações

Scripts de implantação, rotinas de backup e ferramentas de integração contínua usam o SSH tanto quanto as pessoas, e as recomendações para elas são um pouco diferentes.

Uma chave por automação

A recomendação da Mozilla para as chaves de máquina vale aqui: chaves únicas por uso, com privilégios mínimos, registradas num inventário, e longe de contas de root ou com sudo [10]. Na prática, a rotina de backup tem a sua chave, a implantação tem a dela, e cada uma só faz o que precisa, com as opções do authorized_keys: o restrict, um command forçado e um from com o endereço de origem [16].

Fixar a chave do servidor

Automações não respondem à pergunta da primeira conexão, e a tentação é desligar a conferência com StrictHostKeyChecking no. É exatamente o que a RFC descreve como vulnerável a um intermediário [2]. O caminho certo é gravar de antemão a chave do servidor no known_hosts da automação, conferida por um canal confiável, ou usar certificados de host com a marcação @cert-authority [16]. Assim a automação recusa um servidor falso, em vez de entregar a ele os arquivos.

Conexões reaproveitadas

Uma implantação que roda vinte comandos no mesmo servidor faz vinte trocas de chaves e vinte autenticações, a menos que reaproveite a conexão com o ControlMaster e o ControlPersist [12]. O ganho é grande em scripts com muitos passos, e o socket de controle deve ficar numa pasta só da conta da automação [12].

Mantenha a sessão viva, mas não para sempre

Do lado do servidor, o ClientAliveInterval e o ClientAliveCountMax derrubam as sessões de clientes que pararam de responder [15], o que evita que conexões de automações interrompidas fiquem penduradas. Do lado do cliente, o ServerAliveInterval faz o mesmo no sentido contrário [12].

Quando a conexão não funciona

Os problemas de SSH costumam se repetir, e o próprio cliente tem as ferramentas para descobri-los.

Ver o que o cliente está fazendo

A opção -v liga as mensagens de diagnóstico sobre o andamento do cliente, úteis para problemas de conexão, de autenticação e de configuração; repetida, aumenta o detalhe, até o máximo de três vezes [7]. Com ela, o cliente mostra a versão que o servidor anunciou, os algoritmos combinados, a chave de host apresentada, as chaves que ele está tentando e a resposta do servidor a cada tentativa.

ssh -vv usuario@servidor

Ver a configuração que vale

Com um ~/.ssh/config cheio de blocos Host e Match, nem sempre é óbvio qual opção está valendo para um servidor. A opção -G faz o cliente imprimir a configuração depois de avaliar os blocos e sair, sem conectar [7]. É a forma mais rápida de descobrir por que o ProxyJump não está sendo usado, ou qual chave o cliente vai oferecer.

ssh -G servidor-interno | grep -E "proxyjump|identityfile|user "

Ver o que o cliente suporta

A opção -Q lista os algoritmos que o cliente suporta para um recurso, como as cifras, os códigos de autenticação das mensagens, as trocas de chaves e os tipos de assinatura [7]. Ela ajuda a entender uma falha de negociação com um servidor antigo: se o servidor só oferece algoritmos que o cliente não tem mais, a conexão falha antes da autenticação.

ssh -Q kex

Os sintomas mais comuns

Com essas ferramentas, a maior parte dos problemas tem diagnóstico rápido:

  • "Permission denied (publickey)": o servidor não aceitou nenhuma das chaves oferecidas; o -v mostra quais foram tentadas, e o motivo costuma ser a chave errada, a falta do IdentitiesOnly ou permissões erradas no authorized_keys do servidor;
  • "Too many authentication failures": o agente ofereceu chaves demais antes da certa e estourou o MaxAuthTries do servidor, de 6 por padrão; o IdentitiesOnly resolve [12][15];
  • "No matching key exchange method found": cliente e servidor não têm um algoritmo em comum, típico de servidores muito antigos; o -Q e o -v mostram o que cada um oferece;
  • "REMOTE HOST IDENTIFICATION HAS CHANGED": a chave de host mudou; descubra o motivo antes de aceitar a chave nova [7];
  • conexão que cai depois de um tempo parada: um firewall no caminho derruba as conexões ociosas; o ServerAliveInterval mantém a conexão viva [12].

Túneis sem terminal

Para quem usa o SSH só para encaminhar portas, duas opções completam o quadro: o -N não executa nenhum comando remoto, o que é útil quando só se quer o encaminhamento, e o -f manda o cliente para o segundo plano logo antes de executar o comando, depois de pedir a senha ou a frase-senha, se for preciso [7]. O -T desliga a alocação de terminal, útil para comandos não interativos [7].

# um túnel para o banco, em segundo plano, sem terminal
ssh -f -N -L 5433:localhost:5432 usuario@servidor

As falhas que mudaram a configuração

Três incidentes recentes ensinaram muito sobre o SSH, e cada um deixou uma lição diferente.

Diagrama de três incidentes recentes do SSH: o Terrapin, de dezembro de 2023, um ataque de truncamento de prefixo no próprio protocolo, que remove mensagens do início do canal seguro, corrigido pelo strict kex no OpenSSH 9.6; o regreSSHion, de julho de 2024, uma condição de corrida no sshd que permitia executar código como root sem autenticação em Linux com glibc, uma regressão de uma falha de 2006, corrigida no OpenSSH 9.8; e o backdoor do xz, de março de 2024, código malicioso na biblioteca liblzma, que chegava ao sshd por meio do libsystemd em algumas distribuições
Uma falha no protocolo, uma no código e uma na cadeia de suprimentos

Terrapin: uma falha no protocolo

O Terrapin é um ataque de truncamento de prefixo contra o protocolo SSH: manipulando os números de sequência durante o aperto de mão, um atacante consegue remover em silêncio mensagens do início do canal seguro [13]. As conexões vulneráveis eram as que usavam o ChaCha20-Poly1305 ou cifras em modo CBC com autenticação depois da cifra, e o ataque exigia uma posição de intermediário na rede [13]. O impacto principal era remover a mensagem de negociação de extensões, o que permitia rebaixar a segurança da autenticação por chave RSA do cliente e, com o OpenSSH 9.5, desligar a proteção contra ataques de tempo de digitação [13].

O ataque foi publicado em 18 de dezembro de 2023 por Fabian Bäumer, Marcus Brinkmann e Jörg Schwenk, da Universidade do Ruhr em Bochum, e recebeu o identificador CVE-2023-48795 [13]. A contramedida é a troca de chaves estrita, o "strict kex", acrescentada no OpenSSH 9.6, lançado no mesmo dia [5][13]. Ela só funciona quando o cliente e o servidor a suportam [13], e por isso atualizar os dois lados é o que resolve.

A lição: até um protocolo padronizado há quase vinte anos e analisado por muita gente pode ter falhas de projeto. Manter cliente e servidor atualizados não é opcional.

regreSSHion: uma falha no código

Em 1º de julho de 2024, a Qualys publicou o regreSSHion, o CVE-2024-6387, uma condição de corrida no servidor do OpenSSH [9]. Se um cliente não se autentica dentro do LoginGraceTime, de 120 segundos por padrão, o tratador de alarme do sshd roda de forma assíncrona e chama funções que não são seguras naquele momento, como a de registro em log; em sistemas Linux com a glibc, isso podia permitir a execução remota de código como root, sem autenticação [9]. O OpenBSD não era afetado [9].

O nome vem da história: a falha era uma regressão de outra, de 2006, que voltou porque uma alteração de outubro de 2020, que chegou na versão 8.5p1, removeu uma proteção do código [9]. As versões afetadas iam da 8.5p1 à 9.7, e a correção saiu na 9.8 [5][9]. Como mitigação temporária, colocar o LoginGraceTime em zero impedia a execução de código, mas deixava o servidor vulnerável a negação de serviço, porque um atacante podia ocupar todas as conexões simultâneas permitidas [9].

A lição: código maduro também regride. Testes de regressão para falhas antigas, e atualizações rápidas quando um aviso sai, são parte da segurança do SSH.

O backdoor do xz: uma falha na cadeia de suprimentos

Em 29 de março de 2024, o engenheiro Andres Freund avisou a lista oss-security de que o repositório e os pacotes do xz tinham um backdoor, nas versões 5.6.0 e 5.6.1 da biblioteca liblzma [14]. O xz não tem nada a ver com o SSH, mas chegava a ele por um caminho indireto: algumas distribuições modificam o sshd para avisar o systemd, o que faz o sshd carregar o libsystemd, que depende da liblzma [14]. Freund percebeu o problema por um sintoma pequeno: logins por SSH gastando muito processador e erros do valgrind, numa versão de testes do Debian [14].

A lição: a segurança de um servidor SSH depende também das bibliotecas que ele carrega, inclusive as que ninguém imagina que estejam ali. E, às vezes, quem descobre um ataque sofisticado é alguém que estranhou um login meio segundo mais lento.

Um roteiro para proteger um servidor SSH

As seções anteriores se resumem num roteiro.

  1. Manter o OpenSSH atualizado, no servidor e nos clientes: o Terrapin e o regreSSHion foram corrigidos por atualizações [5][9][13].
  2. Aceitar só chaves, com AuthenticationMethods publickey, e desligar de verdade as senhas, inclusive pelo PAM se houver autenticação interativa [10].
  3. Proibir o login de root, com PermitRootLogin no, e usar sudo [10].
  4. Registrar as impressões digitais das chaves usadas, com LogLevel VERBOSE [10].
  5. Usar chaves Ed25519, de preferência em dispositivos físicos para as pessoas, com frase-senha [6][10].
  6. Separar as chaves de máquina, únicas por uso, com privilégios mínimos e inventariadas [10].
  7. Conferir as chaves de host, por impressão digital, SSHFP ou certificados de host, em vez de aceitar no escuro [2][7].
  8. Preferir certificados de validade curta quando houver muitas pessoas e servidores [6].
  9. **Acessar a rede interna por ProxyJump**, sem encaminhar o agente [7][10].
  10. Não publicar serviços internos: usar o encaminhamento local para chegar a eles [7].
  11. Manter a troca de chaves pós-quântica, sem desligá-la no KexAlgorithms [4].
  12. Acompanhar os avisos de segurança do OpenSSH e das bibliotecas do sistema [14].

Erros comuns

  • Aceitar a chave de host na primeira conexão sem conferir. A conexão fica exposta a um intermediário [2].
  • **Apagar a linha do known_hosts quando a chave muda.** Primeiro descubra por que mudou [7].
  • Deixar a autenticação por senha ligada. Senhas são adivinhadas, vazam e são entregues a servidores falsos [8][10].
  • **Achar que PasswordAuthentication no basta.** A autenticação interativa pode reabrir a porta; confira o PAM [10].
  • Chaves sem frase-senha nos notebooks. Quem copia o arquivo entra no servidor [10].
  • A mesma chave para tudo. Separe por propósito [10].
  • **Encaminhar o agente com -A por hábito.** Use o ProxyJump [7][10].
  • Expor o banco de dados à internet para acessá-lo de casa. Use o encaminhamento local [7].
  • Login direto como root. As ações deixam de ser atribuíveis a uma pessoa [10].
  • Chaves DSA ou RSA com SHA-1 em sistemas antigos. O DSA saiu do OpenSSH 10.0; troque por Ed25519 [5][6].
  • Desligar a troca de chaves pós-quântica por compatibilidade. O risco de guardar agora e decifrar depois é real [4].
  • Ficar numa versão antiga do OpenSSH. As falhas recentes foram corrigidas em versões novas [5].
  • **Confiar no padrão do PasswordAuthentication.** Ele vem ligado; desligue explicitamente [15].
  • Chaves de automação com acesso total. Use restrict, command e from no authorized_keys [16].
  • **StrictHostKeyChecking no nas automações.** Grave a chave do servidor de antemão ou use certificados de host [2][16].
  • **Conta de SFTP sem DisableForwarding.** O ForceCommand não bloqueia os encaminhamentos [15].
  • Acesso temporário sem data de fim. Use o expiry-time ou certificados de validade curta [6][16].
  • Chaves no agente para sempre. Defina um tempo de vida com -t [18].
  • Commits sem assinatura num repositório que exige autoria verificável. A mesma chave SSH pode assinar [19].

Conclusão

O SSH nasceu em 1995 para acabar com as senhas trafegando em texto aberto, e a versão 2, padronizada em 2006, organiza o protocolo em três camadas: o transporte, que cifra e autentica o servidor; a autenticação, que identifica o usuário; e a conexão, que divide o túnel em canais [1][2]. A troca de chaves gera um segredo que ninguém na rede consegue calcular, e o OpenSSH já a faz com algoritmos híbridos pós-quânticos por padrão, contra quem grava hoje para decifrar amanhã [3][4].

A segurança de verdade, porém, está em duas conferências que dependem de quem usa. A chave de host garante que o cliente fala com o servidor certo, e aceitá-la no escuro anula a proteção [2]. A chave do usuário prova a identidade sem que a chave privada saia do cliente, e é muito mais forte que a senha [8]. Certificados de validade curta, dispositivos físicos e o ProxyJump levam essas garantias para ambientes com muitas pessoas e servidores [6][7].

Por fim, os incidentes recentes mostram que o SSH falha de três jeitos: no protocolo, como no Terrapin; no código, como no regreSSHion; e na cadeia de suprimentos, como no xz [9][13][14]. A defesa contra os três é a mesma: atualizar, configurar com cuidado e prestar atenção nos sinais estranhos. O SSH é uma das ferramentas mais confiáveis da internet, e continua assim porque muita gente cuida dele.

Para quem administra servidores, o resumo prático cabe em poucas linhas: chaves Ed25519 com frase-senha, de preferência em dispositivos físicos; senhas desligadas de verdade; nada de root direto; chaves de host conferidas ou certificadas; ProxyJump em vez de encaminhar o agente; chaves de automação restritas no authorized_keys; e o OpenSSH sempre atualizado. Com isso, o SSH continua sendo o que foi desde 1995: a forma segura de entrar num computador do outro lado da rede.

Fontes

  1. Secure Shell — en.wikipedia.org
  2. RFC 4251: The Secure Shell (SSH) Protocol Architecture — datatracker.ietf.org
  3. RFC 4253: The Secure Shell (SSH) Transport Layer Protocol — datatracker.ietf.org
  4. OpenSSH: Post-Quantum Cryptography — openssh.org
  5. OpenSSH Release Notes — openssh.org
  6. ssh-keygen(1) — man.openbsd.org
  7. ssh(1) — man.openbsd.org
  8. RFC 4252: The Secure Shell (SSH) Authentication Protocol — datatracker.ietf.org
  9. regreSSHion: RCE in OpenSSH's server, on glibc-based Linux systems (CVE-2024-6387) — qualys.com
  10. OpenSSH Security Guidelines — infosec.mozilla.org
  11. RFC 4254: The Secure Shell (SSH) Connection Protocol — datatracker.ietf.org
  12. ssh_config(5) — man.openbsd.org
  13. Terrapin Attack — terrapin-attack.com
  14. backdoor in upstream xz/liblzma leading to ssh server compromise — openwall.com
  15. sshd_config(5) — man.openbsd.org
  16. sshd(8) — man.openbsd.org
  17. RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol — datatracker.ietf.org
  18. ssh-agent(1) — man.openbsd.org
  19. About commit signature verification — docs.github.com
  20. Get started with OpenSSH Server for Windows — learn.microsoft.com
  21. RFC 8709: Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol — datatracker.ietf.org
  22. RFC 4255: Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints — datatracker.ietf.org

Este post foi escrito com a ajuda da Juniper, a assistente de IA da Gothor, e revisado pela nossa equipe antes de ir ao ar.

Comentários

Ainda não há comentários. Que tal ser o primeiro?

Entre na sua conta para comentar. É grátis.

Receba os próximos posts por e-mail

Um e-mail a cada post novo, sem spam. Você cancela quando quiser.

Para ampliar, use a roda do mouse, os botões, as teclas + e − ou o gesto de pinça; toque duas vezes para ampliar ou voltar. Com a imagem ampliada, arraste ou use as setas para mover. Esc fecha.