Como enviar senha com segurança por HTTP?

115

Se em uma tela de login o usuário enviar um formulário com seu nome de usuário e senha, a senha será enviada em texto simples (mesmo com POST, corrija-me se eu estiver errado).

Portanto, a questão é qual é a maneira correta de proteger o usuário e sua senha contra terceiros que possam estar espionando os dados de comunicação?

Estou ciente de que HTTPS é uma solução para o problema, mas existe alguma maneira de garantir pelo menos algum nível de segurança usando o protocolo HTTP padrão (solicitação POST)? (talvez usando javascript de alguma forma)

EDIT Eu posso ter deixado de fora algumas coisas importantes.

Eu estava tratando de uma página - que é uma página de login gerada por PHP, que é enviada ao usuário em uma solicitação HTTP GET como um arquivo HTML. Não há nenhuma conexão (@Jeremy Powel) estabelecida entre o servidor e o cliente, então não posso criar esse protocolo de handshaking. E eu quero que o processo completo seja transparente para o usuário - ele quer enviar uma senha, não lidar com criptografia.

Obrigado.

Kornelije Petak
fonte
1
Você provavelmente não conseguirá fazer isso sem que o cliente use criptografia, mas o usuário não precisa ver esse processo. Ele apenas insere sua senha e o código que seu PHP gera (javascript por exemplo) trata de tudo para você.
Jeremy Powell,
13
O problema que você descreve é ​​o motivo pelo qual o HTTPS foi inventado. Se você enviar um segredo ao cliente para criptografar a senha, um intruso será capaz de farejá-lo e descriptografar a senha na viagem de volta.
jnoss,
1
Então S na sua sugestão poderia ser apenas senha (ou nome de usuário + senha combinados de alguma forma), pois este é o único "segredo" que o usuário possui. Estou correcto? Então a solução seria a seguinte: - O servidor fornece à página HTML um campo oculto do formulário R - O usuário digita a senha e, antes de enviar a senha, o javascript calcula H (R, S) e a envia para o servidor, talvez até usando AJAX - O servidor calcula H (R, S) e o compara com recebido e envia uma resposta à solicitação de ajax se a autenticação foi aprovada - O javascript redireciona o navegador para a página desejada
Kornelije Petak
2
@jeremy powell - embora o que você descreve seja uma prática comum, também é vulnerável a um intermediário que pode farejar o cookie de um cabeçalho e se passar pelo usuário reutilizando o cookie. É difícil proteger contra ataques man in the middle, a menos que você esteja usando HTTPS
jnoss
1
Para quem quer que tenha essa pergunta no futuro: DEPOIS de fazer login, você também precisa proteger o cookie de sessão. (Então: usar HTTPS é realmente muito mais fácil.)
Arjan

Respostas:

66

Usar HTTP com SSL tornará sua vida muito mais fácil e você poderá ficar à vontade. Pessoas muito inteligentes (pelo menos mais inteligentes do que eu!) Examinaram esse método de comunicação confidencial por anos.

Jeremy Powell
fonte
14
... e "Mas eu tenho que pagar por um certificado SSL !!" não é uma reclamação válida, já que você pode obtê-los por $ 30 hoje em dia. Seus dados realmente não valem 30 dólares para proteger?
caf
3
E se o host da web em que você se inscreveu não oferecer suporte à adição de certificados SSL?
Calmarius
89
@Calmarius - então você muda para um
host
3
@BornToCode Isso significa tecnicamente que você precisa ter um IP dedicado e possuir o hardware do servidor (ou pelo menos um VPS) para usar HTTPS. Os webhosts compartilhados não podem fazer HTTPS, a menos que todo o servidor esteja protegido com o certificado do proprietário do host.
Calmarius,
6
webhosts compartilhados certamente podem fazer https, usando en.wikipedia.org/wiki/Server_Name_Indication
Brian Minton,
39

A autenticação segura é um tópico amplo. Resumindo, como @ jeremy-powell mencionou, sempre favorece o envio de credenciais por HTTPS em vez de HTTP. Isso vai tirar muitas dores de cabeça relacionadas à segurança.

Os certificados TSL / SSL são muito baratos atualmente. Na verdade, se você não quiser gastar dinheiro, existe um letsencrypt.org grátis - Autoridade de Certificação automatizada.

Você pode ir um passo além e usar caddyserver.com, que chama o letsencrypt em segundo plano.

Agora, uma vez que tiramos o HTTPS do caminho ...

Você não deve enviar login e senha via carga útil POST ou parâmetros GET. Em vez disso, use um cabeçalho de autorização (esquema de autenticação de acesso básico), que é construído da seguinte maneira:

  • O nome de usuário e a senha são combinados em uma string separada por dois pontos, por exemplo: nome de usuário: senha
  • A string resultante é codificada usando a variante RFC2045-MIME de Base64, exceto não limitada a 76 caracteres / linha.
  • O método de autorização e um espaço, ou seja, "Básico", são colocados antes da string codificada.

fonte: Wikipedia: cabeçalho de autorização

Pode parecer um pouco complicado, mas não é. Existem muitas bibliotecas boas por aí que fornecerão essa funcionalidade para você fora da caixa.

Existem alguns bons motivos pelos quais você deve usar um cabeçalho de autorização

  1. É um padrão
  2. É simples (depois de aprender como usá-los)
  3. Isso permitirá que você faça login no nível do URL, como este: https://user:[email protected]/login(o Chrome, por exemplo, irá convertê-lo automaticamente em Authorizationcabeçalho)

IMPORTANTE:
Como apontado por @zaph em seu comentário abaixo, enviar informações confidenciais como consulta GET não é uma boa ideia, pois muito provavelmente terminará nos logs do servidor.

insira a descrição da imagem aqui

FullStackForger
fonte
11
O problema de enviar credenciais (senha) como parâmetros GET é que o par usuário / senha provavelmente acabará nos logs do servidor, o que não é uma boa ideia. É melhor enviar credenciais em um POST.
zaph
Ahh ... de jeito nenhum. A captura de tela que você está vendo foi modificada para ilustrar o que está acontecendo em um navegador. No momento em que você clicar em Enter, o navegador irá converter seu url criando um Authorizationcabeçalho. Apenas dê uma chance. Logs lembrarão limpo. E, claro, se você estiver fazendo uma chamada do servidor (se for esse o cenário com o qual está se preocupando), você deve gerar o cabeçalho programaticamente, é claro.
FullStackForger
Você não pode registrar o que não pode ver. username:password@urldo navegador se traduz em: url+ Authorizationcabeçalho da solicitação. Quanto a consultas GET ... bem como eu disse, use o cabeçalho Authroziation. É melhor.
FullStackForger
2
Você não aborda o ponto da questão: proteger dados confidenciais de espionagem do homem no meio. O foco não é encontrar uma forma alternativa e / ou mais padronizada de passar credenciais, mas protegê-las em um canal inseguro.
Lord of the Goo
3
Deve ser enfatizado que esta solução só funciona se você já estiver criptografando a conexão com TLS / SSL. base64 não é criptografia.
Scot
14

Você pode usar um esquema de resposta de desafio. Digamos que o cliente e o servidor conheçam um segredo S. Então o servidor pode ter certeza de que o cliente sabe a senha (sem revelá-la):

  1. O servidor envia um número aleatório, R, ao cliente.
  2. O cliente envia H (R, S) de volta para o servidor (onde H é uma função hash criptográfica, como SHA-256)
  3. O servidor calcula H (R, S) e o compara com a resposta do cliente. Se corresponderem, o servidor sabe que o cliente conhece a senha.

Editar:

Há um problema aqui com a atualização do R e o fato de que o HTTP não tem estado. Isso pode ser resolvido fazendo com que o servidor crie um segredo, chame-o de Q, que apenas o servidor conhece . Então, o protocolo é assim:

  1. O servidor gera um número aleatório R. Ele então envia ao cliente H (R, Q) (que não pode ser forjado pelo cliente).
  2. O cliente envia R, H (R, Q) e calcula H (R, S) e envia tudo de volta para o servidor (onde H é uma função hash criptográfica, como SHA-256)
  3. O servidor calcula H (R, S) e o compara com a resposta do cliente. Então ele pega R e calcula (novamente) H (R, Q). Se a versão do cliente de H (R, Q) e H (R, S) corresponder à recálculo do servidor, o servidor considera o cliente autenticado.

Para notar, uma vez que H (R, Q) não pode ser forjado pelo cliente, H (R, Q) atua como um cookie (e poderia, portanto, ser implementado na verdade como um cookie).

Outra edição:

A edição anterior do protocolo está incorreta, pois qualquer pessoa que observou H (R, Q) parece ser capaz de reproduzi-la com o hash correto. O servidor precisa lembrar quais Rs não são mais recentes. Estou fazendo CW esta resposta para que vocês possam editar e descobrir algo bom.

Jeremy Powell
fonte
+1 - você precisará calcular a resposta do lado do cliente com javascript (ou Flash / Silverlight / etc.)
orip
10
Não impede o homem no meio ou ataques de personificação. Por exemplo, através de wi-fi. Parece que isso só vai dar uma falsa sensação de segurança, IMO.
Tom Hawtin - tackline
2
Isso protege contra ataques passivos, mas um Homem no Meio ainda pode atacar.
Douglas Leeder,
7
Também requer que o servidor saiba a senha original.
Douglas Leeder,
3
Não reinvente a criptografia! (em produção)
bryanph
11

Se o seu host permitir ou você precisará lidar com dados confidenciais, use HTTPS, ponto final. (Muitas vezes é exigido pela lei afaik).

Caso contrário, se você quiser fazer algo por HTTP. Eu faria algo assim.

  1. O servidor incorpora sua chave pública na página de login.
  2. O cliente preenche o formulário de login e clica em enviar.
  3. Uma solicitação AJAX obtém o carimbo de data / hora atual do servidor.
  4. O script do lado do cliente concatena as credenciais, o timestamp e um salt (hash de dados analógicos, por exemplo, movimentos do mouse, eventos de pressionamento de tecla), criptografa-o usando a chave pública.
  5. Envia o hash resultante.
  6. O servidor descriptografa o hash
  7. Verifica se o carimbo de data / hora é recente o suficiente (permite apenas uma janela curta de 5 a 10 segundos). Rejeita o login se o carimbo de data / hora for muito antigo.
  8. Armazena o hash por 20 segundos. Rejeita o mesmo hash para login durante este intervalo.
  9. Autentica o usuário.

Dessa forma, a senha é protegida e o mesmo hash de autenticação não pode ser reproduzido.

Sobre a segurança do token de sessão. Isso é um pouco mais difícil. Mas é possível tornar a reutilização de um token de sessão roubado um pouco mais difícil.

  1. O servidor define um cookie de sessão extra que contém uma string aleatória.
  2. O navegador envia de volta esse cookie na próxima solicitação.
  3. O servidor verifica o valor no cookie, se for diferente, ele destrói a sessão, caso contrário, está tudo bem.
  4. O servidor configura o cookie novamente com um texto diferente.

Portanto, se o token de sessão for roubado e uma solicitação for enviada por outra pessoa, na próxima solicitação do usuário original, a sessão será destruída. Portanto, se o usuário estiver navegando ativamente no site, clicando em links com frequência, o ladrão não irá longe com o token roubado. Este esquema pode ser fortalecido exigindo outra autenticação para as operações confidenciais (como exclusão de conta).

EDIT: Observe que isso não evita ataques MITM se o invasor configurar sua própria página com uma chave pública diferente e solicitações de proxy para o servidor. Para se proteger contra isso, a chave pública deve ser fixada no armazenamento local do navegador ou dentro do aplicativo para detectar esse tipo de truque.

Sobre a implementação: RSA é provavelmente o algoritmo mais conhecido, mas é bastante lento para chaves longas. Não sei quão rápida seria uma implementação de PHP ou Javascript. Mas provavelmente existem algoritmos mais rápidos.

Calmarius
fonte
Neste caso a senha está realmente protegida? Alguém não poderia detectar o que é enviado e descriptografar usando a chave pública e, em seguida, apenas atualizar o carimbo de data / hora quando usá-lo mais tarde? Estou esquecendo de algo?
michaellindahl
A criptografia assimétrica @michaellindahl significa que apenas a chave privada - que nunca sai do servidor - pode ser usada para descriptografar as coisas. As chaves públicas só podem ser usadas para criptografar.
Dan Pantry
2
Um computador entre o navegador e o servidor pode alterar a chave pública na página de login.
Antti
Obrigado por compartilhar seus métodos, achei tudo muito interessante. O carimbo de data / hora adicionado no envio de credenciais é uma boa pegada! Uma questão sobre o uso de cookie de sessão extra cujo valor é uma string aleatória: é como um token apenas para a próxima solicitação?
Victor
4

Eu usaria um sistema de troca de chaves Diffie-Hellman do lado do servidor e do lado do cliente com AJAX ou múltiplos envios de formulário (eu recomendo o primeiro), embora não veja nenhuma boa implementação na Internet. Lembre-se de que uma biblioteca JS sempre pode ser corrompida ou alterada pelo MITM. O armazenamento local pode ser usado para ajudar a combater isso, até certo ponto.

nanofarad
fonte
4

Você pode usar o SRP para usar senhas seguras em um canal inseguro. A vantagem é que mesmo se um invasor farejar o tráfego ou comprometer o servidor, ele não poderá usar as senhas em um servidor diferente. https://github.com/alax/jsrp é uma biblioteca javascript que oferece suporte a senhas seguras sobre HTTP no navegador ou no lado do servidor (via nó).

Brian Minton
fonte
1

HTTPS é tão poderoso porque usa criptografia assimétrica. Esse tipo de criptografia não só permite que você crie um túnel criptografado, mas também pode verificar se está falando com a pessoa certa, e não com um hacker.

Aqui está o código-fonte Java que usa a cifra assimétrica RSA (usada pelo PGP) para se comunicar: http://www.hushmail.com/services/downloads/

torre
fonte
0

você pode usar SSL para o seu host, há um projeto gratuito para SSL como o letsencrypt https://letsencrypt.org/

mk990
fonte
2
Verifique a terceira frase em questão. (Além disso, sempre leia outras respostas para se certificar de que não está adicionando uma duplicata menos detalhada.)
Nathan Tuggy
0

Usar https parece a melhor opção aqui (os certificados não são tão caros hoje em dia). No entanto, se http for um requisito, você pode usar alguma encriptação - encriptá-la no lado do servidor e decript no navegador do usuário (enviar chave separadamente).

Usamos isso durante a implementação do safevia.net - a inscrição é feita nos lados dos clientes (remetente / destinatário), de modo que os dados dos usuários não estão disponíveis na rede nem na camada do servidor.

parvuselephantus
fonte