Autenticação por desafio e resposta no SSH

Introdução

Acredito que o SSH não precise ser apresentado e tampouco detalhes de sua instalação. Até porque em praticamente todas as instalações ele já vem instalado por padrão, seja o cliente ssh ou o servidor. Em algumas o servidor precisa ser explicitamente instalado, como é o caso do Ubuntu. Mas não se apresse, é provável que você não precise de um servidor SSH em seu console, já que este é um recurso desejável justamente em servidores.

O SSH funciona sobre o protocolo SSL, da mesma forma que o HTTPS. Como o uso do SSH é feita por administradores e não por usuários leigos, é extremamente incomum o uso de certificados digitais neste caso. Afinal um administrador de redes experiente não irá cair na pegadinha do ataque do homem do meio (onde uma outra máquina se faz passar pelo teu servidor).

Durante o primeiro acesso seria prudente que o cliente verificasse se realmente é o servidor. Observe no exemplo a seguir as mensagens gerados pelo servidor no primeiro acesso:

$ ssh [email protected]
The authenticity of host ‘10.2.3.4 (10.2.3.4)’ can’t be established.

RSA key fingerprint is ff:7b:35:74:cd:4c:59:0b:4b:b5:cf:fe:eb:f4:ec:7a.

Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘10.2.3.4,10.2.3.4’ (RSA) to the list of known hosts.

Observe que ele lhe pede que confirme que este é mesmo o servidor. Neste ponto, se tiver certeza (e pode verificar o fingerprint), ao digitar “yes” a chave do servidor será armazenada em ~/.ssh/know_hosts.

A partir deste momento, nenhuma outra mensagem de aviso ocorrerá, salvo se o servidor for reinstalado ou se estiver mesmo sobre um ataque do homem do meio. Mas aí a mensagem será de recusar a conexão.

Meu objetivo não é exatamente descrever o SSH em si, mas sim alguns recursos pouco usados do mesmo. Um deles é o login por desafio/resposta usando uma chave pública e privada.

Como funciona o desafio resposta

Basicamente consiste em o servidor enviar um desafio para você que somente você poderia resolver. Se você acertar o desafio, passou no teste e pode autenticar-se.

Para explicar melhor o que é o desafio resposta, costumo usar um exemplo didático, porém extremamente fraco. Serve para explicar a ideia, mas não deve jamais ser usado por ser frágil.

Suponha que o cliente possua um segredo que é um número inteiro. Podemos chamá-lo de senha. O servidor conhece este segredo e permitirá login para qualquer um que provar conhecê-lo. A autenticação poderia ser da forma clássica:

  1. o cliente conta para o servidor a sua senha;
  2. o servidor verifica se ele acertou a senha;
  3. servidor aceita ou não o login.

Porém o método anterior possui um problema: a senha é enviada pela rede e alguém poderá capturar ela e se logar no futuro como sendo o cliente.

A mesma solução anterior, porém baseada em desafio e resposta, poderia ser:

  1. servidor envia uma expressão matemática para cliente resolver usando o seu segredo;
  2. cliente resolve a expressão e envia apenas a resposta;
  3. servidor, que também conhece o segredo, vê se cliente acertou a resposta;
  4. se acertou, aceita login.

Pode parecer a mesma coisa, mas a ideia é que a expressão matemática mude aleatoriamente a cada solicitação. Se alguém estiver capturando o tráfego não poderá usar a resposta em um login futuro, pois o servidor irá solicitar outra expressão matemática. Esta expressão pode ser uma simples multiplicação, como demonstra a figura:

Linux: Autenticação por desafio e resposta no ssh

O exemplo da figura anterior ilustra como é a técnica, mas não deve ser usado. Isto porque um atacante conseguirá descobrir muito facilmente a chave, bastando dividir 4305 por 123, pois ele viu o desafio e viu a resposta. Operações matemáticas que possam ser revertidas não devem ser usadas (divisão reverte a multiplicação, soma reverte a subtração). Se usar outra operação mais complexa que não possa ser desfeita e com números realmente muito grandes a técnica poderá ser usada.

Um exemplo de uma operação matemática irreversível é a de módulo, usada no RSA. Apesar de fugir um pouco do foco deste artigo, pois ainda está na minha gaveta um artigo sobre RSA, vale o exemplo:

Linux: Autenticação por desafio e resposta no ssh

O exemplo anterior ainda não pode ser usado. Poderia se os números fossem realmente grandes, com um X em torno de 512 bits de tamanho e o desafio também. Com números pequenos um ataque de força bruta seria possível, isto é, o atacante tentaria todos os possíveis valores de X até achar um que encaixe na expressão.

Já me estendi demais neste conceito, já que o objetivo era apenas explicar o princípio do desafio e resposta. No que diz respeito ao ssh, o desafio e resposta é baseado em chaves públicas e privadas.

Desafio e resposta no ssh

O ssh é suficientemente seguro se bem usado. Se você tem certeza da idoneidade do servidor e se tem certeza de que realmente é o servidor, tudo bem. Não há qualquer problema você digitar sua senha de acesso. Ela não poderá ser lida por mais ninguém.

Isto porque toda a sessão ssh é protegida por um forte algoritmo de criptografia simétrico. Quando você digita sua senha ela é cifrada com este algoritmo e transmitida pela Internet. Alguém, em posição de capturar o tráfego, não conseguirá obter esta senha.

Porém existe ainda uma forma de executar o login sem precisar enviar a senha. E uma forma segura. Mas porque não enviar a senha? Vários motivos.

Primeiro uma segurança a mais, pois agora a senha sequer viaja pela Internet, nem mesmo cifrada. Na hipótese, mesmo remota, de alguém estar aplicando um ataque do homem do meio ou de que o servidor estar corrompido, o atacante realmente não terá sua senha, de forma alguma.

Um segundo motivo pode ser a facilidade de manutenção. As vezes é chato ter que ficar colocando várias vezes a senha, principalmente se todos fizerem a coisa certa, de não usar a mesma senha em todos os servidores (por experiência pessoal, sou cético quanto a isto!).

Para resolver este problema o ssh possui o login por desafio e resposta através do uso de um par de chaves pública e privada. Você primeiro deve criar um par de chaves para si, uma chave pública e privada. A chave privada você mantém em segredo, guarda-a em segurança. Já a chave pública você deposita no servidor e diz para o ssh aceitar conexões de qualquer um que prove ser o conhecedor da chave privada.

O conceito dos algoritmos assimétricos garante que tudo que foi cifrado com a chave pública, apenas a chave privada poderá abrir. Assim o desafio gerado pelo servidor passa a ser o seguinte:

  1. servidor escolhe aleatoriamente uma sequência de bits grande (exemplo: 2048 bits aleatórios);
  2. servidor cifra estes bits com a chave pública do suposto cliente e envia;
  3. cliente precisa dizer ao servidor qual era a sequência escolhida;
  4. cliente só pode fazer isto com sucesso se conhecer a chave privada.

Este método é ainda melhor que o descrito antes, pois não envolve a senha do usuário em nenhum momento, apenas a chave privada dele. Esta chave passará a ser a parte sensível e deve ser protegida. Como ninguém conseguirá decorar uma chave de milhares de bits, ela precisa ser armazenada.

Armazenar é um problema, pois pode-se supor até o roubo do HD onde um atacante teria a chave e se logaria em seus servidores. Para evitar isto, o ssh permite que você proteja sua chave com uma frase de passagem, que você poderá lembrar-se. Pode ser qualquer sequência de caracteres que será solicitada pelo seu programa cliente quando você fizer o login.

Configuração do ssh para autenticação por desafio e resposta

A autenticação por desafio resposta no ssh deve estar habilitada no ssh server. Por minha experiência, todos os servidores ssh já vem com isto habilitado, mas convém verificar. Apenas verifique se a informação PubkeyAuthentication no /etc/ssh/sshd_config não está em “no”. Normalmente ela está comentada no arquivo, o que significa que está ativa (o padrão é permitir):

# grep Pubkey /etc/ssh/sshd_config
#PubkeyAuthentication yes

Se ela estiver comentada ou com “yes”, tudo bem. Mas se estiver com “no”, comente a regra ou mude para “yes” e reinicie o ssh server.

Com o servidor configurado para aceitar esta forma de autenticação, deve-se ainda realizar os seguintes passos:

  1. criar um par de chaves
  2. instalar a tua chave pública no servidor

Usarei o usuário fulano como teste neste artigo.

Criação do par de chaves

Usuário fulano tem um login e senha no servidor 10.2.3.4. O cliente é a máquina didaké (nome do meu notebook). Primeiro ele se logou com seu usuário e senha, só para testar:

$ ssh 10.2.3.4
The authenticity of host ‘10.2.3.4 (10.2.3.4)’ can’t be established.
RSA key fingerprint is fb:a9:ac:b6:3d:3f:42:76:11:cc:44:2f:7f:55:49:97.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘10.2.3.4’ (RSA) to the list of known hosts.
Password:
Have a lot of fun…

Voltando a máquina cliente, a didaké, ele cria seu par de chaves com o ssh-keygen. Os parâmetros indicam criar chaves usando o algoritmo dsa e com 1024 bits de tamanho. Se você não especificar o DSA, o algoritmo RSA será usado. O protocolo RSA não é usado por padrão no ssh e não funcionará a menos que você coloque o parâmetro RSAAuthentication yes no /etc/ssh/sshd_config. Este é desabilitado por padrão.

$ ssh-keygen -b 1024 -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/fulano/.ssh/id_dsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/fulano/.ssh/id_dsa.
Your public key has been saved in /home/fulano/.ssh/id_dsa.pub.
The key fingerprint is:
4e:e4:ab:5c:5d:06:58:83:95:4a:77:1d:a0:7d:e7:ba fulano@didake
The key’s randomart image is:

+--[ DSA 1024]----+ 
|         o+..o.. | 
|        ooo+. .  | 
|       .ooo.. . .| 
|       o.  . . o | 
|        S   o   .| 
|       o o o   . | 
|        + .   .  | 
|     . o       . | 
|      o       E  | 
+-----------------+

Quando ele perguntar “Enter file in which to save the key (/home/fulano/.ssh/id_dsa): ” simplesmente dê um enter aceitando esta sugestão.

Como frase de passagem eu coloquei “Viva o Linux 2009″. Pode ser qualquer coisa, mas tome cuidado pois você deve digitar exatamente a mesma coisa quando solicitado. Se digitar “Viva o LINUX 2009” já não será a mesma frase, pois Linux está tudo em caixa alta. Desaconselha-se deixar sem frase de passagem, mas é uma configuração possível.

Instalação da chave pública no servidor

A instalação da chave pública no servidor consiste em simplesmente colocar ela dentro do arquivo ~/.ssh/authorized_keys. Este arquivo pode ter várias chaves públicas, não apenas uma.

Para realizar esta tarefa o usuário fulano precisará logar-se no servidor, usando ainda sua senha. A chave pública criada está em ~/.ssh/id_dsa.pub no cliente e home do fulano. Ela é texto puro (base64) logo pode-se até dar um cat nela:

$ cat id_dsa.pub
ssh-dss AAAAB3NzaC1kc3MAAACBAKaOHlNYzozRRr0Ub1kAi/mBap55fQCda0t4T4rZnskdqX /gMnKvLP8B514b3Oq8exNCkTym6nyv1LyxxG1VmhpjKN8N8eDaErp/8qEif/GvH3HaFx4GJBWFed4Z6K9HkxsBy2yFwfcBmkvGFP3ggMwhBnKYFf7DSa9/0QzHDDx3AAAAFQDEb5c3RMsEo8xU6YsvVXnIlJteLQAAAIA+3Oa2X2oW2gprZkCRe7JE +KWvH+O9xjep/7l6iGFww9IDj35VgEHMzAr6LPvw+rAOB1P5qoXZr3hbTU6pzfHiSLy6UD G+LzHLRbyImZOH/p+n7hWtEfZs6mO5ZuJFxO3fStQKWy0r48XUEaduHY5PGQc+sa/fpjyS2BPToM46XgAAAIAzsUHfOZnFhGK5cmMGjEFyjQBIKqYxRo3pR2H0au/ObwrKan6rCEqWropJElI234AeIymOS2h4Hr3lHnbmyXsxxCt14xqE7ZQlq5X7DV2QCl 0PUcgGlbVSLNTGLXapW1KN2tiBv7u8Q0agNgZ3ek3XHTKtZ6jVm0eb/ACsg0D+Sw== fulano@didake

Não faça o mesmo para a chave privada, pois ela não é em formato texto, é binário.

Então, basta pegar esta sequência esquisita de caracteres e copiá-la, assim como está, para o servidor. No Gnome pode-se até mesmo usar o recurso de copiar e colar do sistema, como demonstra a sequência de imagens.

Linux: Autenticação por desafio e resposta no ssh

Selecionando a chave pública no cliente, arquivo ~/.ssh/id_dsa.pub e usando o “Copy” do próprio Gnome.

Linux: Autenticação por desafio e resposta no ssh

Após, loga-se no servidor 10.2.3.4 ainda usando sua senha, e coloca-se a chave dentro do arquivo authorized_keys em ~/.ssh. No caso eu usei o simples cat com redirecionamento (cat >> authorized_keys).

Linux: Autenticação por desafio e resposta no ssh

Ao final, após colar, deve-se pressionar Control+D e o arquivo está com a chave pública. Na imagem, após o término, executei um cat para verificar.

Outras tantas formas podem ser usadas para obter-se o mesmo resultado, como simplesmente editar o arquivo authorized-keys com algum editor qualquer. O fato é que ele deve ter a chave pública.

Por fim, após a instalação, pode-se verificar se está funcionando. fulano sai do servidor e tenta-se logar novamente:

$ ssh 10.2.3.4
Enter passphrase for key ‘/home/fulano/.ssh/id_dsa’:
Last login: Mon Jul 27 13:40:02 2009 from 10.1.0.10
Have a lot of fun…

Agora ele pediu a frase de passagem que protege a chave pública. No caso eu tive que digitar o “Viva o Linux 2009” para poder me logar.

Conclusão

O uso desta forma de autenticação é considerado mais seguro que com usuário e senha. Porém a maior vantagem, na minha opinião, é ser bem mais prático.

Prático porque você pode colocar senhas diferentes em seus servidores e logar-se neles apenas por desafio e resposta, decorando apenas a sua frase de passagem. Pode colocar sua chave pública em todas as máquinas Linux que você deseja acessar.

Mais prático também porque hoje muitos chaveiros permitem armazenar a frase de passagem, solicitando-a apenas uma única vez por sessão e fazendo realmente um login sem senha nas conexões posteriores. É o caso do Gnome (e não uso KDE para testar).

Muitas coisas fantásticas podem ser realizadas com esta técnica, algumas com bastante cuidado, evidente. Eu, por exemplo, em um curso de Linux, criei um par de chaves e o coloquei no /root/.ssh/authorized_keys de todas as máquinas dos alunos. Assim, a partir do meu notebook, eu podia executar remotamente qualquer comando nas máquinas e sem senha, pois usava o lembrete de senhas do Gnome.

Para desligar uma máquina, bastou um:

$ ssh [email protected] “/sbin/shutdown -h now”

Isto dentro de um for, permite executar em todas as máquinas. Um exemplo mais interessante foi quando precisei que os alunos tivessem poderes de root as vezes, mas não sempre. Quando eu queria lhes dar esta permissão para todas as 20 máquinas, executava do meu notebook:

$ for IP in `cat ubuntu.txt`; do
ssh root@$IP “/usr/sbin/adduser aluno admin”
done
echo OK

E dentro do arquivo ubuntu.txt tinha os IPs dos clientes, um por linha (nota: no ubuntu o adduser pode ser usado para inserir um usuário já existente em um grupo). Se, depois, não quero mais que o usuário aluno tenha poderes de root, então:

$ for IP in `cat ubuntu.txt`; do
ssh root@$IP “/usr/sbin/deluser aluno admin”
done
echo OK

Isto porque no Ubuntu para poder usar o sudo basta estar no grupo admin (e no ubuntu o deluser pode ser usado para remover um usuário de um grupo. Nesta forma de uso, o usuário em si não é removido, apenas sai do grupo).

Se você gerencia dezenas de máquinas pode ser extremamente criativo e deixar os scripts trabalharem por você.

Rolar para cima