Encoding, Hashing e Encryption: Qual a diferença?
Tags: kotlin, braziliandevs, security
Published: 2022-01-22 22:34:16

Encoding, hashing e encryption são conceitos comuns aplicados e discutidos ao tentar proteger os dados. Muitos devs afirmam usar métodos e padrões de criptografia fortes, mas é necessário que uma equipe de segurança avalie se realmente é apropriado.👌

Vamos dar uma olhada nas diferenças entre o uso adequado de encoding, hashing e encryption.

## Encoding
_Encoding_(codificar) algo é comunicá-lo de uma maneira que o receptor entenda. Existem muitos padrões de codificação, como por exemplo, **Base64**, **UTF-8** e **ASCII**. Cada padrão tem uma finalidade e as aplicações que usam esses padrões de codificação esperam receber dados compatíveis com esse padrão de codificação.

Uma comparação fácil é a linguagem humana. As palavras, sintaxe e regras da linguagem são diferentes e únicas para cada cultura linguística. Um nativo de português pode não ser capaz de ler ou falar japonês, mas com o tradutor adequado, ele pode decodificar o japonês para o português e até codificar o português para o japonês usando um tradutor.

As duas linhas a seguir representam os mesmos dados:

**Texto:** Esta linha tem dados secretos que você realmente deve codificar.
**Base64:** RXN0YSBsaW5oYSB0ZW0gZGFkb3Mgc2VjcmV0b3MgcXVlIHZvY8OqIHJlYWxtZW50ZSBkZXZlIGNvZGlmaWNhci4=

Para os olhos humanos, essas duas linhas são totalmente diferentes. Para uma aplicação, elas são iguais.

Se você codificar diversas vezes os mesmos dados, o encode gerado será o mesmo valor, caso mude uma vírgula, irá mudar tudo.

A codificação de dados é normalmente usada para garantir a integridade e usabilidade dos dados e na maioria das vezes é usada quando os dados não podem ser transferidos em seu formato atual entre sistemas ou aplicações.

**A codificação não é usada para proteger dados, porque é fácil de reverter.**

Qualquer fornecedor que afirme usar criptografia base64 forte em sua solução é suspeito.

Exemplo de código em kotlin:
```kotlin
import java.util.*

fun main() {
    val mensagem = "Esta linha tem dados secretos que você realmente deve codificar."

    //codifica para base64
    val codificarString: String = Base64.getEncoder().encodeToString(mensagem.toByteArray())

    println("string codificada: $codificarString")

    //decodifica
    val decodificarBytes = Base64.getDecoder().decode(codificarString)
    val decodificarString = String(decodificarBytes)

    println("string decodificada: $decodificarString")
}
```



## Hashing
Hashing é um método de validação de integridade. O problema que o hashing se propõe a resolver não é proteger os dados de serem lidos, mas sim validar que os dados em questão não foram alterados.

Os algoritmos de hash são funções unidirecionais que recebem uma entrada e calculam uma única saída. Os algoritmos de hash são projetados de tal forma que você não pode pegar um valor de hash e trabalhar o algoritmo de volta para obter a entrada original. Caso haja uma invasão na sua base de hash, o invasor teria que gerar muitas entradas em potencial e comparar a saída com a saída do algoritmo de hash para ver se elas estavam corretas.

Os algoritmos de hash incluem SHA1, SHA256 e SHAKE256, entre outros.

As três linhas a seguir representam os mesmos dados:

**Texto**: Esta linha tem dados secretos que você realmente deve criptografar.
**MD5**: ca37111d05a53346b2c14d749e4c4191
**SHA1**: f6472fbf25720494e4d02d0ec2c94aacee34d602

Hashing nunca deve ser revertido. Ele não deve ser usado para impedir que as comunicações sejam lidas por partes não autorizadas.

Um uso comum de hash é para proteger o armazenamento de senhas. Quando um usuário cria uma senha, a senha é colocada por meio de um algoritmo de hash. O hash resultante é então armazenado. Quando um usuário tenta se autenticar, a senha é criptografada novamente e comparada com o hash armazenado.

**Novamente, o hash é uma verificação de integridade. Não é criptografia.**

Exemplo de código em kotlin:
```kotlin
import java.math.BigInteger
import java.security.MessageDigest

fun main() {

    val input = "Esta linha tem dados secretos que você realmente deve criptografar."
    val md5 = input.md5()
    println("o hash do md5 é $md5")

    val sha1 = input.sha1()
    println("o hash do sha1 é $sha1")
}

fun String.md5(): String {
    val md = MessageDigest.getInstance("MD5")
    return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
}

fun String.sha1(): String {
    val md = MessageDigest.getInstance("SHA-1")
    return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
}
```



## Encryption
A _Encryption_(criptografia) torna os dados ilegíveis por qualquer pessoa, exceto aqueles que conhecem a _secret key_(chave secreta). O segredo pode ser uma única _shared key_(chave compartilhada) ou pode ser dividido em duas chaves - uma mantida privada e outra fornecida à outra parte.

Digamos que Quinn e Gary desejam se comunicar com segurança. Eles decidem usar uma _shared key_(chave compartilhada) de “chookity” para seu segredo. Ao criptografar uma mensagem, Quinn insere a chave de criptografia. Ao descriptografar a mensagem, Gary digita a chave para poder ler a mensagem.

Quinn e Gary, no entanto, estão preocupados com o fato da mensagem secreta ser lida por um terceiro que possa adivinhar ou obter essa chave. Eles decidem usar um sistema de duas chaves (criptografia de chave assimétrica - _asymmetric key encryption_) para criptografar as mensagens. Quinn quer enviar uma mensagem que somente Gary pode ler. Quinn usa uma chave (chave pública - _public key_) que Gary deu a ela com antecedência. Gary recebe essa mensagem criptografada e usa sua outra chave (chave privada - _private key_) para descriptografar e ler essa mensagem. Como apenas Gary conhece sua chave privada e nunca a compartilhou com ninguém, ninguém pode descriptografar a mensagem, exceto Gary. Gary também usa sua chave privada para enviar uma mensagem para Quinn e Quinn pode usar a chave pública que Gary deu a ela para lê-la. Esse tipo de criptografia é conhecido como _signing_(assinatura). Como a chave pública que Gary deu a Quinn descriptografou a mensagem, Quinn pode presumir com segurança que Gary enviou a mensagem, pois apenas Gary tem a outra chave desse par.

Algoritmos de criptografia fortes comuns incluem: AES, Blowfish e RSA.

## Conclusão
Encoding, hashing e encryption podem ser usados ​​juntos. Uma mensagem codificada em base64 para uma aplicação pode ser hash para que a integridade dessa mensagem possa ser verificada pelo receptor. A mensagem pode então ser criptografada e enviada para o destinatário que irá descriptografar e comparar o hash da mensagem com o valor de hash recebido do remetente para garantir que a mensagem não foi alterada.

Compreender a diferença entre esses conceitos pode ajudá-lo a considerar o design e a arquitetura de segurança, especialmente quando se trata de aquisição ou revisão de aplicações. Implementar acidentalmente a codificação como um método de criptografia pode ser muito perigoso para uma organização, pois isso significaria que as transmissões são dados reais que podem ser facilmente decodificados por qualquer pessoa, então tomem cuidado senhores & senhoras ✌️