Segurança em banco de dados - Você realmente está administrando os dados da melhor forma?
A segurança dos dados são essenciais para o sucesso e a reputação das empresas. Neste artigo, exploraremos os pilares da segurança da informação relacionados a bancos de dados discutindo tópicos vitais, desde planos para desastres e backup até criptografia em repouso, e algumas boas práticas visando deixar nossos DBs mais seguros.
O foco do texto será sobre a segurança em bancos de dados relacionais (como o MySQL). Não abordaremos sobre bancos NoSQL, pois caso contrário ficaríamos até o amanhecer aqui, então isso fica para outro artigo, ok?
O que você vai ver neste artigo:
- Pilares da segurança da informação (CID)
- Planos para desastres e configuração de backup de dados
- Protegendo seus dados contra erros acidentais com SQL TCL
- Privilégio mínimo de acesso e comandos SQL DCL
- Criptografia em repouso e hashing de senhas
CID - A tríade da segurança da informação
Antes de abordarmos o tema principal deste artigo, vamos explorar alguns conceitos essenciais em segurança da informação. Esses princípios são encapsulados na tríade CID, que representa os pilares de Confidencialidade, Integridade e Disponibilidade. Vamos examinar cada um deles:
- Confidencialidade: se refere à proteção das informações contra o acesso não autorizado. Isso significa garantir que apenas pessoas ou sistemas autorizados tenham acesso a dados sensíveis.
- Integridade: está relacionada à garantia de que os dados não tenham sido alterados ou corrompidos de forma não autorizada. Vou te dar um exemplo para entender melhor: suponhamos que Joãozinho tirou nota 3 em uma prova que está cadastrada em um banco de dados, e ele conseguiu de forma indevida e não autorizada modificar essa nota para 10, isso representa uma quebra de integridade.
- Disponibilidade: diz respeito à garantia de que os sistemas e recursos de informação estejam sempre disponíveis e acessíveis quando necessário. Isso envolve proteger os sistemas contra falhas, ataques de negação de serviço (DDoS), desastres naturais e outros eventos que possam afetar a disponibilidade.
A seguir, iremos explorar de maneira abrangente como esses conceitos são fundamentais para o planejamento de um banco de dados seguro. Além disso, discutiremos as ferramentas e as melhores práticas essenciais para garantir a confidencialidade, integridade e disponibilidade dos nossos dados.
Recuperação de dados: backups e planos para desastres
Falamos brevemente sobre o pilar de disponibilidade na seção acima e é esta a “dor de cabeça” que iremos trabalhar melhor neste tópico. A manutenção da disponibilidade dos dados de um banco de dados é uma preocupação fundamental, pois inúmeros problemas podem resultar na perda desses dados, como falhas de sistema, erros de hardware, exclusão (acidental ou intencional) por parte dos usuários e corrupção de dados.
Dependendo da criticidade do seu sistema, a indisponibilidade de dados pode custar caro, especialmente em aplicações financeiras, onde as perdas podem ser exorbitantes. Para garantir a recuperação rápida dos dados e evitar consequências potencialmente catastróficas, é essencial criar um plano sólido de recuperação de desastres.
Embora o termo "desastre" possa evocar imagens de destruição, ele não se limita a situações de perigo extremo. Um desastre pode ser, por exemplo, um programador que, sem querer, apaga todos os registros de uma tabela em um banco de dados de produção - um acontecimento que parece bobo, mas que pode causar muitos problemas.
Não duvide, isso realmente pode acontecer. Teve um caso que viralizou em 2016 sobre um rapaz que apagou todos os servidores e dados da empresa (inclusive os backups!) por acidente ao executar uma linha de comando indevida (veja em: 'Cara, cadê a firma?': rapaz 'deleta' empresa com linha de código errada - TecMundo).
Um plano eficiente para recuperação de dados deve ser o mais simples possível, facilitando a implementação em situações de crise. É importante que o plano leve em consideração as prioridades de serviços que devem ser restaurados. Por exemplo, no caso de uma falha em um sistema bancário, é necessário que as informações de contas e usuários sejam os primeiros serviços a serem restabelecidos.
Veja mais sobre o assunto em: Chapter 4 - IT Disaster Recovery Plan - Ivan Cordero Torres
Um dos componentes fundamentais para o sucesso de um plano de recuperação de desastres é a réplica de dados (ou o famoso “backup”). Um bom inventário de todos os dados, incluindo réplicas, de uma empresa é imprescindível. Para recuperar dados após uma falha total, é preciso saber onde os dados estão armazenados. Ah, e não se esqueça, ter apenas 1 backup ou backups sem testar é o mesmo que ter nada.
Vejamos abaixo algumas formas de configurar backups para bancos de dados.
Backups no MySQL Workbench
O MySQL Workbench é uma ferramenta com ambiente gráfico famosinha para administrar bancos de dados MySQL. Ele já vem com um serviço de backup que, por padrão, executa automaticamente à meia-noite. Esse recurso pode ser desativado e nem sempre dá para contar com ele. Quem tem só 1 backup não tem backup algum, lembre-se disso.
Caso você queira configurar um backup de forma manual usando o workbench, você pode executar os seguintes passos:
- Conecte-se ao banco de dados no Workbench;
- Selecione “Server” na barra de ferramentas, e em seguida selecione “Data Export”;
- Selecione o database e as tabelas que deseja fazer o backup. Neste caso, estou fazendo o backup do banco “colecao” e todas as tabelas que existem nele. Observe que você pode escolher se quer realizar o backup somente das estrutura do banco, somente dos dados ou os dois.
- Se existirem stored procedures, eventos ou triggers em seu banco, também há a opção de incluí-los no backup. Como meu banco não possui esses objetos, deixarei desmarcada essas opções.
- Em Export Options, selecione onde você pretende salvar o backup. Por padrão, o arquivo fica na pasta Documents em uma subpasta chamada dumps. Também há a opção de criar um único arquivo com todas as tabelas, mas neste caso manterei o default.
- Clique em “Start Export”, e veja a mágica acontecer: o Workbench criará uma réplica bonitinha das tabelas do seu banco.
- Ao acessar a pasta de destino informada no “Export Options”, podemos visualizar os arquivos .sql das tabelas que exportamos.
Fizemos uma réplica das tabelas do nosso banco de dados, mas quem garante que ela está funcionando? Para evitar dores de cabeça futuras, você sempre deve testar o bendito backup, e para isso vamos ver como realizar o restore do arquivo que exportamos no Workbench:
- Com o Workbench devidamente conectado, já até a área “navigator” localizada no canto esquerdo da tela, abra a janela “Administration” e selecione “Data Import/Restore”
- No campo “Import from Dump Project Folder”, selecione a localização dos arquivos (se você exportou usando os valores padrões, provavelmente esses arquivos estarão dentro de uma pasta com o nome “dump”). Selecione o banco e as tabelas que deseja restaurar, vá em “import progress” e clique em “start import”
Atente-se que a restauração do banco de dados sobrescreverá todos os dados caso você tenha um database com o mesmo nome.
Backups no PHPMyAdmin
O PHPMyAdmin é outra ferramenta popular para administrar bancos de dados MySQL com ambiente gráfico e de fácil uso. A forma de realizar backups manuais com ele não é tão diferente do que vimos até então. Acompanhe os seguintes passos:
- Conecte-se com seu banco e acesse o endereço da página do PHPMyAdmin - se você estiver executando em um servidor local ou com o XAMPP, o link será algo como localhost/phpmyadmin;
- Selecione o banco que deseja realizar o backup, e clique em “Exportar”
- Há dois métodos de exportação: rápida e personalizada. Por padrão, ele exporta todas as tabelas e dados contidos no banco. Caso queira exportar apenas algumas tabelas específicas, ou se você quiser apenas estrutura ou só os dados, isso pode ser configurado no modo personalizado.
- Uma opção interessante do PHPMyAdmin é que ele te permite exportar os dados em outros formatos além do .SQL, como o PDF. Mas para testarmos o restore do banco, vamos manter os arquivos no padrão SQL.
Para fazer o caminho inverso, basta escolher a opção “Importar” e selecionar o arquivo que foi criado pelo PHPMyAdmin. Você precisará antes criar um novo database manualmente e indicar que está usando ele. Você pode usar a guia “SQL” e digitar o seguinte comando:
CREATE DATABASE <nome_do_db>;
USE <nome_do_db>;
Pode ser que você precise editar algumas linhas do arquivo SQL gerado, por conter linhas que não são reconhecidas como comandos ou comentários ditos pelo SGDB.
Automação de rotinas de backup
Simulamos algumas rotinas de backup, mas em todos os casos executamos ela de forma manual. Fazer tarefas rotineiras e cruciais como essa dessa forma é correr o risco de dar alguma coisa errada por falha humana. É por isso que quase sempre que o assunto é segurança, é mencionado como boa prática automatizar tarefas como essa.
Um exemplo de como rotinas de backup podem ser automatizadas é criando scripts bash, caso você utilize um servidor Linux. Vejamos um exemplo de código:
//crie um diretório para salvar os arquivos de backup, e dentro dele, abra seu editor de texto de sua preferência (como nano, vi, ou outros)
mkdir -p /backup/mysql
cd /backup/mysql
nano
Dentro do editor de texto, escreva o seguinte código (as partes com “#”, com exceção do !#/bin/bash, são comentários):
#!/bin/bash
FILE=backup.sql.`date +"%Y%m%d"`
# Aqui se cria a variável FILE, que receberá o nome do arquivo de backup com a data do dia em que foi criado. O formato "backup.sql.YYYYMMDD" garante que cada backup tenha um nome exclusivo com base na data.
DBSERVER=127.0.0.1
# Aqui se insere o endereço IP do servidor. No caso, o servidor é local e está no IP local. Caso esteja configurado nos hosts da máquina, em vez do IP, também se pode utilizar o localhost, como no código comentado abaixo.
# DBSERVER=localhost
DATABASE=database-name
# Substitua o “database-name” pelo nome do seu banco de dados. Se formos pegar o exemplo acima, colocaríamos o nome “cotacao”.
USER=user-name
# Substitua o “user-name” pelo nome do usuário que possui acesso ao banco de dados. Pode ser o usuário padrão “root”. Falaremos sobre criação de usuários e permissões mais adiante.
PASS=your-password
# Substitua o “your-password” pela senha do usuário inserido na linha anterior.
unalias rm 2> /dev/null
rm ${FILE} 2> /dev/null
rm ${FILE}.gz 2> /dev/null
# Nas 3 linhas acima, garante-se que não tenha um arquivo já com o nome criado na pasta em que o backup será feito.
mysqldump --opt --user=${USER} --password=${PASS} ${DATABASE} > ${FILE}
# Executa-se então o programa mysqldump, que é uma ferramenta nativa do mysql para gerenciar backups. As variáveis USER, PASSWORD, DATABASE e FILE referem-se aos parâmetros que informamos nas primeiras linhas do documento.
gzip $FILE
# Fará com que o arquivo seja comprimido com o Gzip. Isso economizará espaço em disco.
echo "${FILE}.gz was created:"
# Avisa o usuário que o arquivo foi criado.
ls -l ${FILE}.gz
# Lista-se então o arquivo criado.
Salve o arquivo como “script_backup.sh”, e depois mude o permissionamento para permitir que ele seja executado. Você pode utilizar o comando chmod +x <caminho do arquivo> , mas neste caso vou atribuir um permissionamento para que apenas o proprietário do arquivo consiga usá-lo, uma vez que ele contém informações sensíveis de usuário e senha.
chmod 700 ./script_backup.sh
Agora que temos o script pronto, vamos fazê-lo funcionar. Vamos configurar um cron job para que ele execute o backup todos os dias. Para isso, digite o seguinte comando no terminal:
crontab -e
Dentro do arquivo, digite as seguintes linhas:
0 1 * * * /backup/mysql/script_backup.sh.sh 1>> /var/log/mysqlbackup.log 2>>/var/log/mysqlbackup-error.log
# O campo “0 1 * * *” significa que o arquivo será executado todos os dias da semana em todos os meses à 1h da manhã. Em seguida é mostrado a localização do script que será executado, indicando saídas para logs do mysql.
Este é apenas um exemplo de script, mas podem ser aplicadas melhorias. Alguns pontos a se considerar é a criptografia e controle de acesso aos arquivos de backup, gerar logs mais detalhados, e também a questão de gerenciamento de senhas, que seria mais adequado usar ferramentas como o mysql_config_editor ao invés de armazenar a senha de usuário diretamente no arquivo do script.
Também existem ferramentas de terceiros (tanto open-source quanto comerciais), como o SQLBackupAndFTP, que administram e automatizam as tarefas de backup de banco de dados. Quem sabe exploramos elas em outro artigo?
Onde armazenar meus arquivos de backup?
Na maioria dos exemplos trabalhados até então, estivemos armazenando os arquivos das réplicas localmente, na mesma máquina onde se encontra o serviço de banco de dados. Isso pode nos ajudar a reverter possíveis erros acidentais no banco, mas se você precisar planejar um plano para desastres conciso, você vai precisar ser um pouco mais pessimista que isso. Se seu servidor morrer, seu backup morre junto.
Discos extras, fitas para backup, armazenamento na nuvem, tudo vale, o que não vale é ter apenas uma mídia de backup. Se você quer manter a disponibilidade dos seus dados, leve pro seu coração: quem tem só 1 backup não tem nenhum, e quem tem 2 backups talvez não tenha nenhum também.
Quanto às mídias citadas anteriormente, vamos abrir alguns comentários
- Particionamento de Disco - Tentador, pois é bastante econômico, uma vez que não precisa de hardware extra, e a recuperação dos dados é rápida, mas eu particularmente nem considero isso como forma de backup, porque a redundância garantida por esta mídia é quase inexistente. Se houver falha no disco, seu servidor e seu backup vão para o além juntos. Meu conselho é: não confie neste método.
- Discos Extras - Uma solução um pouco melhor que o particionamento de discos, uma vez que os arquivos estarão em um dispositivo diferente. Bastante usados para a configuração de RAIDs. A recuperação de dados é relativamente rápida e contam com a vantagem de serem portáteis e poderem ser armazenados off-site para segurança adicional.
- Em contrapartida, a compra de discos adicionais pode gerar um custo elevado, adicionar complexidade no gerenciamento de espaço físico à medida que os backups aumentam, além de existir o risco de falhas de hardware.
- Fitas de backup - Essa daqui talvez seja uma das formas mais tradicionais de se fazer backup. As fitas de backup geralmente têm maior capacidade de armazenamento, mais vida útil e são menos vulneráveis a danos por água e fogo se comparadas com os discos ou outras formas de armazenamento.
- No entanto, elas também têm um custo mais salgado e a recuperação dos dados é relativamente lenta. Do mesmo modo há a necessidade de gerenciamento de espaço físico.
- Armazenamento em nuvem - Esse aqui talvez seja um dos métodos que eu mais recomendo. O armazenamento em nuvem é altamente escalável e redundante e pode ser acessado a partir de qualquer lugar pela internet. Serviços populares de armazenamento em nuvem incluem AWS S3, Google Cloud Storage e Azure Storage.
- Alguns provedores de cloud também possuem serviços de bancos de dados SQL nativos onde é muito fácil configurar opções de replicação de dados, como o AWS RDS.
- Mas fique atento, como você vai armazenar seus dados em dispositivos de terceiros, você precisa verificar antes se não existem políticas de conformidade que te limitam a usar este tipo de serviço. Além disso, ele não deve ser a única forma de backup, uma vez que você é obrigado a ter conexão com a internet para acessar os dados.
Independente de como você faça seu backup, lembre-se de sempre testá-lo. Backup sem testar é o equivalente a não ter backup. E sempre que possível, tente automatizar tarefas relacionadas a isso. Também crie políticas de retenção, para planejar por quanto tempo uma determinada mídia de réplicas deve existir.
Protegendo seus dados contra erros acidentais com SQL TCL
Vou te contar um segredo caso você não saiba: em bancos de dados SQL não existe CTRL + Z. Isso significa que se você apagar ou atualizar algum dado por acidente, a menos que você tenha um backup e restaure ele, a sua tabela vai ficar do mesmo jeito, não importa quantas vezes você aperte as teclas do “desfazer”.
Mas não se desespere, existe uma funcionalidade em SGDBs para deixar seu trabalho um pouco menos difícil neste quesito: as transactions. Elas funcionam como uma espécie de sandbox onde você consegue manipular seu banco de dados sem afetar os dados em produção (a menos que especificado), e você também consegue reverter algumas ações enquanto está dentro dela.
Vejamos alguns dos principais comandos que podem ser executados em uma transaction:
- BEGIN TRANSACTION ou simplesmente BEGIN para iniciar uma transaction.
- ROLLBACK para reverter uma transação e desfazer todas as suas alterações. Esse aqui seria o mais próximo de um “ctrl + z” que você pode ter em um banco de dados.
- COMMIT para confirmar uma transação e aplicar permanentemente suas alterações ao banco de dados. Cuidado que a partir daqui não tem mais choro e nem ROLLBACK que te salve se você fez algo errado.
- SAVEPOINT para criar pontos de salvamento intermediários dentro de uma transação, permitindo que partes específicas da transação sejam revertidas.
Em alguns SGDBs como o MariaDB, statements como “DELETE”, “DROP” ou “TRUNCATE” não podem ser revertidos, mesmo com o ROLLBACK. Então não se emocione demais ao usar uma transaction, porque ela não fará milagre nestes casos. Mas tirando isso, é um ótimo quebra-galho para manter a integridade dos dados, não acha?
Privilégio mínimo de acesso
Vamos ver outro assunto muito relevante quando se trata de segurança de banco de dados, que é o controle de acesso. Quando você configura um Sistema Gerenciador de Banco de Dados (SGDB) pela primeira vez, você utiliza um usuário padrão geralmente denominado de “root”. Este usuário padrão tem permissões ilimitadas e pode realizar qualquer tarefa administrativa dentro do banco de dados.
O fato do usuário padrão ter acesso irrestrito é justamente o motivo que o torna um tanto perigoso. Se as credenciais desse usuário forem violadas, a pessoa que conseguir o acesso ao banco de dados poderá fazer o que quiser nele. Ou, em um caso mais rotineiro, uma pessoa interna poderia apagar dados que não deveria acidentalmente.
Na própria documentação do MySQL, é recomendável que você NÃO use o root para tarefas administrativas. Uma boa prática é criar usuários separadamente para cada função e com privilégios restringidos, não dando permissão para coisas que eles não precisam. Isso é algo que também é defendido pelo conceito de privilégio mínimo de acesso
Além disso, usuários sem senhas ou que não são usados, e bancos de dados “abandonados” são coisas que não devem existir por serem potenciais interfaces de ataque. Mais um mantra para você levar para seu coração: “Uma janela desnecessária é uma porta de convite para um ladrão, então se não usa, desinstala e apaga”.
Comandos SQL DCL
Para atribuir o permissionamento específico de ações e acessos a bancos de dados/tabelas para cada usuário, usa-se uma categoria de comandos chamada DCL (Data Control Language). Vejamos os comandos existentes:
- GRANT: este comando é usado para conceder permissões a usuários ou papéis em objetos de banco de dados, como tabelas, visualizações, procedimentos armazenados, etc.
- Sintaxe básica:
GRANT <permissão> ON <nomeDoDB.nomeDaTabela> TO <nomeUsuario@endereço>;
- REVOKE: este comando é usado para remover permissões anteriormente concedidas de usuários ou papéis.
- Sintaxe básica:
REVOKE <permissão> ON <nomeDoDB.nomeDaTabela> FROM <nomeUsuario@endereço>;
- DENY: Embora este comando não seja parte padrão do SQL, alguns SGDBs o suportam. Ele é usado para negar explicitamente permissões a usuários ou papéis, e suas permissões prevalecem sobre as permissões concedidas com GRANT.
- Sintaxe básica:
DENY <permissão> ON <nomeDoDB.nomeDaTabela> TO <nomeUsuario@endereço>;
A sintaxe dos comandos pode variar dependendo do SGBD que você está utilizando.
Prática - criando usuários e atribuindo permissões a eles
Nem só de teoria se vive o homem, então vamos ver na prática como criar usuários em um banco de dados. Para este exemplo, estarei usando o MySQL workbench por ter uma visualização mais agradável, mas os mesmos comandos podem ser executados via terminal. Em outros SGDBs, pode ser que a sintaxe mude um pouco, mas vamos lá:
Crie seu primeiro usuário usando o seguinte comando:
CREATE USER 'teste'@'localhost' IDENTIFIED BY 'k1W2p3r0';
Aqui, “teste” é o nome do usuário que estamos criando, “localhost” é o host onde ele será criado (pode ser um IP de um servidor de banco de dados) e “k1W2p3r0” é a senha atribuída a este usuário. É possível deixar a senha do usuário em branco, mas isso não é uma prática recomendada.
Para visualizar quais usuários existem em sua instância, assim como as permissões atribuídas a eles, execute o comando:
SELECT * FROM mysql.user;
Esses usuários com nomes esquisitos como “mysql.infoschema”, “mysql.session” e “mysql.sys” são usuários internos que são responsáveis pelo funcionamento de determinados recursos no banco de dados. A exclusão ou modificação desses usuários pode resultar no comprometimento do funcionamento do MySQL como um todo. Não é pra apagar eles, hein!
O “root” é o mesmo root que mencionamos no tópico de privilégio mínimo de acesso. Se você quer deixar seu banco seguro, ele obrigatoriamente precisa ter uma senha e não deve ser utilizado para tarefas rotineiras. Usa ele para criar outros usuários, depois deixa ele isolado.
Criamos nosso primeiro usuário “teste”, mas observe que ainda não atribuímos nenhum privilégio a ele. Ele só existe, mas não pode fazer tarefa alguma. Vamos atribuir permissões a este usuário:
(Você pode ver a lista de privilégios do MySQL através deste link: MySQL :: MySQL 8.0 Reference Manual :: 6.2.2 Privileges Provided by MySQL)
GRANT ALL PRIVILEGES ON *.* TO 'teste'@'localhost';
O asterisco (*) neste comando é um caracter coringa que quer dizer “tudo”. Neste caso, estamos atribuindo todos os privilégios do SGDB a este usuário em todos os bancos de dados e tabelas. Mas nós já aprendemos que isso é algo perigoso, e a boa prática é atribuir somente os privilégios que aquele usuário realmente precisa para trabalhar.
Vamos excluir o usuário teste, e criar um novo usuário atribuindo permissões de forma mais adequada:
DROP USER 'teste2'@'localhost';
CREATE USER usuario@'localhost' IDENTIFIED BY 'k1W2p3r0';
GRANT CREATE, SELECT, INSERT, UPDATE ON colecao.game TO 'usuario'@'localhost';
FLUSH PRIVILEGES; /* Este comando serve para validar os privilégios atribuídos */
Neste caso, criamos um usuário chamado “usuario”, que possui permissão de executar os comandos CREATE, SELECT, INSERT, UPDATE somente na tabela “game” do database “colecao”. Este seria um exemplo de criação de usuário mais adequado.
Caso um usuário tente executar uma ação ao qual ele não tenha direito, ele será impedido e receberá uma mensagem de erro parecida com a abaixo:
Error Code: 1142. DELETE command denied to user 'usuario'@'localhost' for table 'game'
Se precisarmos tirar uma permissão de um usuário, podemos usar o comando REVOKE como no exemplo abaixo:
REVOKE CREATE, SELECT, INSERT, UPDATE ON *.* FROM 'usuario'@'localhost';
FLUSH PRIVILEGES;
E, por fim, para visualizar quais permissões um usuário possui, execute o comando:
SHOW GRANTS FOR 'usuario'@'localhost';
Outro recurso bastante útil no MySQL são as roles, que representam uma coleção nomeada de privilégios. Da mesma forma que as contas de usuário, as roles podem ter privilégios atribuídos ou retirados. Além disso, é possível associar uma conta de usuário a uma ou mais roles, concedendo automaticamente os privilégios correspondentes a cada role.
Se você possui vários usuários que executam tarefas parecidas, este recurso vai ser uma ótima forma de facilitar o concedimento de permissões sem afetar a segurança (contado que você aplique as boas práticas de privilégio mínimo).
Vejamos abaixo como é feita a criação e atribuição de roles para usuários:
CREATE ROLE 'app_developer'
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_developer';
GRANT 'app_developer' TO 'usuario'@'localhost';
Caso haja outros usuários, podemos reaproveitar a role “app_developer” para atribuir-lhe os mesmos permissionamentos do usuário “usuario”.
O que é criptografia?
Estamos na reta final do artigo, e agora vamos explorar um assunto que é de extrema importância no que tange a confidencialidade dos seus dados: ela mesma, a criptografia! Neste caso, mais especificamente a criptografia em repouso.
Dando uma introdução geral ao tópico, criptografia trata-se de uma prática utilizada para codificar alguma mensagem (pode ser texto, imagem, vídeos, etc) através de uma chave, tornando-a inelegível para quem não possui a chave para descodificar essa mensagem.
Entenda como “chave” um valor único que funciona como se fosse uma "senha" para codificar ou decodificar uma informação.
Explicando de forma breve, existem dois tipos principais de criptografia: simétrica e assimétrica.
Na criptografia simétrica, a mesma chave é usada tanto para criptografar quanto para descriptografar os dados. Isso significa que qualquer pessoa com acesso à chave pode ler os dados.
AES, DES, IDEA e Blowfish são exemplos de algoritmos que utilizam criptografia simétrica
Na criptografia assimétrica, envolve o uso de um par de chaves: uma chave pública para criptografar os dados e uma chave privada correspondente para descriptografá-los. A chave pública pode ser compartilhada livremente, enquanto a chave privada deve ser mantida em sigilo.
Alguns exemplos de algoritmos que utilizam criptografia assimétrica são: Diffie-Hellman, RSA e ECC.
Usamos a criptografia para garantir que, mesmo se alguém conseguir acessar os nossos bancos de dados, eles não possam ler o conteúdo sem a chave de descriptografia correta, garantindo a confidencialidade dos dados. No caso de banco de dados, chamamos isto de "criptografia em repouso".
Configurando criptografia no MySQL
Para o exemplo, iremos utilizar o algoritmo de criptografia simétrica AES, mas primeiro, vamos criar um database e uma tabela do zero, para em seguida populá-los com dados. Vamos lá:
CREATE DATABASE criptografia_exemplos;
USE criptografia_exemplos;
CREATE TABLE cadastro_usuarios (
id INT PRIMARY KEY AUTO_INCREMENT,
usuario VARCHAR(30),
senha VARBINARY(100)
/* O tipo de dado precisa ser um BLOB para que haja compatibilidade com o AES */
);
Para utilizar a encriptação AES no MySQL, chamamos a função AES_ENCRYPT e passam-se dois argumentos: o texto que será criptografado e a senha para descriptografar, respectivamente. A instrução ficaria da seguinte maneira:
INSERT INTO cadastro_usuarios (usuario, senha) VALUES ('Usuário AES', AES_ENCRYPT('senha123', 'Minha senha secreta' ));
Ao resgatar os dados com SELECT, teremos o seguinte retorno:
Para fazer o caminho inverso, deveremos usar a função AWS_DECRYPT, e juntamente converter o dado do tipo BLOB para algo legível, como no exemplo abaixo:
SELECT
CAST(AES_DECRYPT(senha, 'Minha senha secreta') AS CHAR(255))
FROM cadastro_usuarios
WHERE senha is not null;
Note que neste exemplo eu não estou seguindo as melhores práticas de segurança. No campo onde seria informada a chave privada, eu basicamente usei uma senha. Em um cenário ideal, eu deveria utilizar um HSM (Hardware Security Module) ou um uma solução de gerenciador de senhas (como o AWS KMS) para gerar uma chave mais segura.
Outra questão é que este tipo de criptografia não é a mais adequada para armazenar senhas. Veremos abaixo um recurso mais indicado para este caso.
O que é hashing e por que devo usar ele para armazenar senhas?
Vimos anteriormente o uso de criptografia em repouso para garantir a confidencialidade dos nossos dados. No entanto, senha é um tipo de dado que precisa de uma camada extra de segurança, onde a criptografia tradicional não é adequada.
Como se trata de um tipo de dado classificado como “sensível”, pelo princípio de confidencialidade ninguém deve ser capaz de acessá-lo, nem mesmo os administradores do sistema ou o próprio usuário. Se você já solicitou a redefinição de senha em algum site, e ele te enviou um email com sua senha antiga, saiba que ali tem um probleminha com segurança, hein!
Para resolver esse tipo de problema, utilizamos funções de hashing para armazenar senhas. A diferença do hash para a criptografia padrão é que ele é unidirecional, isso significa que uma vez que o dado foi criptografado, não tem mais volta, não há como decodificar o conteúdo (pelo menos, não diretamente).
Configurando hash em dados do MySQL
O MySQL dispõe de funções de hashing nativas para MD5, SHA-1 e SHA-2. Discutiremos abaixo sobre a eficácia de segurança deles mais tarde, mas por ora, usaremos o SHA-2 por ser o mais seguro dos 3.
Vamos utilizar a função de hashing no campo “senha”, uma vez que ele é considerado um dado sensível. Para usar a função, basta usar a sintaxe: sha2(<valor>, <tamanho da criptografia em bits>), onde o tamanho em bits pode variar de 224 até 512.
ALTER TABLE cadastro_usuarios ADD COLUMN senha_hash TEXT;
INSERT INTO cadastro_usuarios (usuario, senha_hash) VALUES ('Usuário SHA-2 ', sha2('senha123', 224));
Ao dar um SELECT na tabela, podemos ver o campo da senha devidamente criptografado:
Uma boa prática é usar salt junto com os algoritmos de hash. Trata-se de um valor aleatório que você coloca junto ao valor original, afim de garantir que mesmo que existam 2 senhas idênticas no banco, o seu valor de hashing nunca seja igual.
Além da criptografia a nível de banco de dados que vimos agora (também chamada de criptografia em repouso), é possível também atribuir essa função de criptografar os dados com funções de hashing para o nível de código ou mesmo hardware.
Dá pra usar qualquer algoritmo de hashing para guardar senhas?
Um dos casos de uso mais comuns para o uso de funções hash é o armazenamento de senhas, mas esta não é a única finalidade dele, inclusive, existem alguns algoritmos que não são adequados para o intuito de armazenar senhas, e outros que não são adequados para caso algum por conta de suas limitações.
Dos algoritmos “populares” que temos hoje, o que eu certamente NÃO te recomendaria para usar para armazenar senhas seriam o MD5 e o SHA-1. Ambos são vulneráveis a ataques de dicionário e força bruta, possuem colisão (dois diferentes inputs que produzem o mesmo hash) e bases de dados de senhas comuns utilizando essas funções. O SHA-2 é um pouco melhor que esses 2, mas também não é uma boa escolha para este cenário.
Algumas funções como o SHA-256 e SHA-512, embora sejam muito mais seguras que um MD5 da vida, são exemplos de algoritmos cujo intuito principal não é o de armazenar senhas. O cálculo delas é relativamente rápido, e rapidez neste caso só é uma coisa boa para atacantes que estejam tentando roubar sua conta via força-bruta.
O Argo2 foi eleito o melhor algoritmo para armazenar senhas no Password Hashing Competition em 2015 (veja em: Password Hashing Competition - HandWiki). O bcrypt e scrypt também são escolhas decentes. Caso você tenha a opção, opte por algumas dessas funções com o uso de salt.
Conclusão
Ufa, foi bastante coisa, não é? Segurança é um assunto bastante extenso e provavelmente nem tivemos tempo para abordar tudo o que tem de importante deste tema, mas tenho certeza que o que vimos até então foi bastante significativo.
Aprendemos sobre os pilares da segurança da informação (o tal do CID), entendemos a importância de um plano conciso de recuperação de desastres e a como configurar de forma simples backups de bancos de dados, a fim de manter a disponibilidade dos nossos dados.
Também vimos sobre o conceito de privilégio mínimo de acesso e exercitamos boas práticas com os comandos TCL. Além disso, falamos sobre a criptografia e como ela nos ajuda a manter a confidencialidade dos dados, e como armazenar senhas de forma segura.
Caso queira aprofundar seus conhecimentos em segurança de banco de dados, recomendo pesquisar sobre SQL Injection e também NoSQL Injection.
Espero que este artigo tenha sido útil para você! Não deixe de dar seu feedback nos comentários!
Referências
- Material didático do curso técnico de análise e desenvolvimento de sistemas - SENAC EAD - Acesso restrito
- What is CIA in Cybersecurity | Deepwatch
- Chapter 4 - IT Disaster Recovery Plan - Ivan Cordero Torres
- MySQL :: MySQL Workbench Manual
- TCL Commands in SQL - Scaler Topics
- MySQL :: MySQL 8.0 Reference Manual :: 6.1.3 Making MySQL Secure Against Attackers
- MySQL :: MySQL 8.0 Reference Manual :: 6.2.2 Privileges Provided by MySQL
- Ten Tips on How to Achieve MySQL and MariaDB Security | Severalnines
- Comandos DCL SQL e sua sintaxe - Bóson Treinamentos em Ciência e Tecnologia (bosontreinamentos.com.br)
- What Is Data Encryption? (Definition, Best Practices & More) (digitalguardian.com)
- MySQL :: MySQL 8.0 Reference Manual :: 12.13 Encryption and Compression Functions