Não é todos os dias que se podem aplicar as lições da criptanálise da Segunda Guerra Mundial às suas próprias aplicações Web. Mas hoje, é exatamente isso que vamos fazer. De facto, vamos ver como até uma das mais poderosas ferramentas criptográficas modernas pode ser derrotada pelo mesmo método que Alan Turing utilizou para quebrar a máquina Enigma - que era a ferramenta criptográfica mais poderosa do seu tempo - se os implementadores cometerem os mesmos erros na transmissão de mensagens que Turing encontrou.
Vejamos uma API que avaliei recentemente, para ver como evitou o problema que ajudou os Aliados a vencer as potências do Eixo na Segunda Guerra Mundial.
Uma API de comércio eletrónico: Um pote de mel muito doce
O exemplo que vou analisar hoje é a API de uma empresa de comércio eletrónico para aceitar pagamentos. No que respeita a alvos de ataque, não há muitos mais valiosos para os agentes maliciosos do que uma API de pagamento por cartão de crédito. É por isso que esta empresa decidiu expor ao mundo uma chave pública através da qual os consumidores da API poderiam encriptar todas as informações de cartões de crédito submetidas para processamento de pagamentos.
É isso mesmo, todos os dados submetidos por este formulário seriam encriptados e nunca enviados em texto simples. Isto porque é importante proteger todos os dados contra ataques, não apenas o número do cartão. A data de validade e o CVV fazem parte do pacote que torna único qualquer método de pagamento específico, pelo que esta empresa tinha tudo encriptado. A empresa nunca enviaria nenhum destes dados em texto simples.
Aqui está o aspeto de um pedido de amostra que atravessou o fio, utilizando um Charles Proxy para capturar o tráfego como um man-in-the-middle:
Observe neste exemplo que há dois valores com hash: paymentHash e cvvHash. Vamos pensar nos dados que estão contidos nesses dois valores.
Para paymentHash, temos o número do cartão de crédito de 16 dígitos, encriptado utilizando a chave pública publicada para utilização nesta API. Obviamente, haverá uma grande variação nos valores disponíveis para a encriptação.
Para cvvHash, não temos a mesma situação! Os CVVs têm apenas 3 dígitos. Se apenas encriptar combinações de três dígitos, um atacante pode construir um dicionário completo de apenas 1000 valores diferentes (10 valores para cada um dos 3 dígitos). Com esse dicionário, pode encontrar o CVV de qualquer cartão enviado através desta API. Felizmente, o meu cliente sabia como evitar ataques de texto simples conhecidos e já tinha implementado o preenchimento aleatório para evitar o mesmo ataque que derrubou os nazis.
Decifrar o enigma
Em parte, a máquina Enigma era tão difícil de decifrar porque tinha uma estrutura muito complexa para encriptar mensagens e as definições eram frequentemente alteradas. No entanto, uma vez que a máquina era utilizada para mensagens militares, a equipa de Turing em Bletchley Park começou a procurar texto que poderia ser esperado em muitas das mensagens.
Um tipo de mensagem que era frequentemente enviado pelos militares alemães era um relatório meteorológico, e estes relatórios incluíam a palavra alemã para "tempo" no mesmo sítio em todas as mensagens, seguida das condições meteorológicas conhecidas. Outro exemplo é o facto de muitas mensagens terminarem com "Heil Hitler" e de alguns operadores usarem saudações padrão. Estes exemplos também permitiram determinar alguns dos textos simples e definições para cada dia.
Este tipo de vulnerabilidade de encriptação é o que poderia estar presente se esta empresa de comércio eletrónico não tivesse utilizado preenchimento aleatório para o CVV, porque o número de valores possíveis é muito pequeno.
Evitar o mesmo erro
Ao utilizar um pad aleatório, a empresa garantiu que dois métodos de pagamento com o mesmo CVV não revelariam nada sobre o que estava a ser transmitido. Eis alguns exemplos de como o mesmo valor de "777" poderia ser encriptado para ter valores diferentes utilizando um pad:
$ echo -n 777 | openssl rsautl -inkey public_key.pem -pubin -pkcs |
Como pode ver, os comandos executados acima são todos iguais, mas devido aos padrões em vigor na encriptação RSA, os valores que são produzidos são todos completamente diferentes.
Bons algoritmos de encriptação não são suficientes
Este exemplo deve ajudá-lo a compreender que não basta pôr em prática um bom esquema de encriptação. Mesmo com algoritmos de encriptação altamente sofisticados, se a mesma entrada for utilizada repetidamente, é possível que isso exponha os dados que está a tentar transmitir de forma segura. É muito importante, especialmente quando se trata de informações de pagamento, certificar-se de que não é fácil adivinhar os valores que estão a ser enviados através do cabo.
Outro exemplo em que o preenchimento é importante é o armazenamento de palavras-passe. Os programadores sabem que precisam de utilizar uma boa função de hash unidirecional para se certificarem de que nem eles conseguem ler as palavras-passe que os seus utilizadores submetem. Para isso, os programadores devem adicionar uma cadeia aleatória (chamada salt ) a qualquer palavra-passe antes de a fazer hash.
Conclusão
Criar segurança nas suas aplicações Web é uma tarefa extremamente importante. Assegurar que os dados sensíveis dos seus utilizadores não são facilmente desencriptados cria confiança nos clientes e garante a longevidade do seu negócio. Afinal de contas, ninguém quer a cobertura mediática de que o seu sítio web divulgou informações de pagamento dos clientes. Se conseguir certificar-se de que não encripta exatamente os mesmos valores uma e outra vez, evitará um erro muito básico e manterá os dados dos seus clientes seguros.
Para saber mais sobre as práticas recomendadas de segurança, confira o extenso conjunto de guias de segurança da Linode!
Comentários