Os .class
arquivos Java podem ser descompilados com bastante facilidade. Como posso proteger meu banco de dados se tiver que usar os dados de login no código?
Os .class
arquivos Java podem ser descompilados com bastante facilidade. Como posso proteger meu banco de dados se tiver que usar os dados de login no código?
Respostas:
Nunca codifique senhas em seu código. Isso foi mencionado recentemente no Top 25 dos erros de programação mais perigosos :
Codificar uma conta secreta e uma senha em seu software é extremamente conveniente - para engenheiros reversos qualificados. Se a senha for a mesma em todo o software, todos os clientes ficarão vulneráveis quando essa senha for inevitavelmente conhecida. E porque é codificado, é uma grande dor de se consertar.
Você deve armazenar informações de configuração, incluindo senhas, em um arquivo separado que o aplicativo lê ao iniciar. Essa é a única maneira real de evitar que a senha vaze como resultado da descompilação (nunca compile-a no binário para começar).
Para obter mais informações sobre esse erro comum, você pode ler o artigo CWE-259 . O artigo contém uma definição mais completa, exemplos e muitas outras informações sobre o problema.
Em Java, uma das maneiras mais fáceis de fazer isso é usar a classe Preferences. Ele é projetado para armazenar todos os tipos de configurações do programa, algumas das quais podem incluir um nome de usuário e uma senha.
import java.util.prefs.Preferences;
public class DemoApplication {
Preferences preferences =
Preferences.userNodeForPackage(DemoApplication.class);
public void setCredentials(String username, String password) {
preferences.put("db_username", username);
preferences.put("db_password", password);
}
public String getUsername() {
return preferences.get("db_username", null);
}
public String getPassword() {
return preferences.get("db_password", null);
}
// your code here
}
No código acima, você pode chamar o setCredentials
método após mostrar uma caixa de diálogo solicitando o nome de usuário e a senha. Quando você precisa se conectar ao banco de dados, você pode apenas usar os métodos getUsername
e getPassword
para recuperar os valores armazenados. As credenciais de login não serão codificadas em seus binários, portanto, a descompilação não representará um risco de segurança.
Observação importante: os arquivos de preferência são apenas arquivos XML de texto simples. Certifique-se de tomar as medidas adequadas para evitar que usuários não autorizados vejam os arquivos brutos (permissões do UNIX, permissões do Windows etc.). No Linux, pelo menos, isso não é um problema, porque a chamada Preferences.userNodeForPackage
criará o arquivo XML no diretório inicial do usuário atual, que de qualquer forma não pode ser lido por outros usuários. No Windows, a situação pode ser diferente.
Notas mais importantes: Houve muita discussão nos comentários desta resposta e em outros sobre qual é a arquitetura correta para esta situação. A pergunta original não menciona realmente o contexto no qual o aplicativo está sendo usado, então falarei sobre as duas situações nas quais consigo pensar. O primeiro é o caso em que a pessoa que usa o programa já conhece (e está autorizada a saber) as credenciais do banco de dados. O segundo é o caso em que você, o desenvolvedor, está tentando manter as credenciais do banco de dados em segredo da pessoa que usa o programa.
Primeiro caso: o usuário está autorizado a saber as credenciais de login do banco de dados
Nesse caso, a solução que mencionei acima funcionará. A Preference
classe Java armazenará o nome de usuário e a senha em texto simples, mas o arquivo de preferências só poderá ser lido pelo usuário autorizado. O usuário pode simplesmente abrir o arquivo XML de preferências e ler as credenciais de login, mas isso não é um risco de segurança porque o usuário já sabia as credenciais.
Segundo caso: tentar ocultar as credenciais de login do usuário
Este é o caso mais complicado: o usuário não deve saber as credenciais de login, mas ainda precisa acessar o banco de dados. Nesse caso, o usuário que executa o aplicativo tem acesso direto ao banco de dados, o que significa que o programa precisa saber as credenciais de login com antecedência. A solução que mencionei acima não é apropriada para este caso. Você pode armazenar as credenciais de login do banco de dados em um arquivo de preferências, mas o usuário poderá ler esse arquivo, pois será o proprietário. Na verdade, não existe uma boa maneira de usar esse gabinete de maneira segura.
Caso correto: usando uma arquitetura multicamadas
A maneira correta de fazer isso é ter uma camada intermediária, entre o servidor de banco de dados e o aplicativo cliente, que autentica usuários individuais e permite que um conjunto limitado de operações seja executado. Cada usuário teria suas próprias credenciais de login, mas não para o servidor de banco de dados. As credenciais permitiriam acesso à camada intermediária (a camada de lógica de negócios) e seriam diferentes para cada usuário.
Cada usuário teria seu próprio nome de usuário e senha, que poderiam ser armazenados localmente em um arquivo de preferências sem qualquer risco de segurança. Isso é chamado de arquitetura de três camadas (as camadas sendo seu servidor de banco de dados, servidor de lógica de negócios e aplicativo cliente). É mais complexo, mas realmente é a maneira mais segura de fazer esse tipo de coisa.
A ordem básica das operações é:
getInventoryList
.Observe que, em todo o processo, o aplicativo cliente nunca se conecta diretamente ao banco de dados . A camada de lógica de negócios recebe uma solicitação de um usuário autenticado, processa a solicitação do cliente para uma lista de inventário e só então executa uma consulta SQL.
Coloque a senha em um arquivo que o aplicativo irá ler. NUNCA incorpore senhas em um arquivo de origem. Período.
Ruby tem um módulo pouco conhecido chamado DBI :: DBRC para tal uso. Não tenho dúvidas de que o Java tem um equivalente. De qualquer forma, não é difícil escrever um.
Você está escrevendo um aplicativo da web? Em caso afirmativo, use o JNDI para configurá-lo externamente ao aplicativo. Uma visão geral está disponível aqui :
O JNDI fornece uma maneira uniforme para um aplicativo localizar e acessar serviços remotos na rede. O serviço remoto pode ser qualquer serviço corporativo, incluindo um serviço de mensagens ou um serviço específico de aplicativo, mas, é claro, um aplicativo JDBC está interessado principalmente em um serviço de banco de dados. Depois que um objeto DataSource é criado e registrado com um serviço de nomenclatura JNDI, um aplicativo pode usar a API JNDI para acessar esse objeto DataSource, que pode então ser usado para se conectar à fonte de dados que representa.
Não importa o que você faça, as informações confidenciais serão armazenadas em algum arquivo em algum lugar. Seu objetivo é torná-lo o mais difícil possível. O quanto disso você pode conseguir depende do seu projeto, necessidades e espessura da carteira da sua empresa.
A melhor maneira é não armazenar nenhuma senha em qualquer lugar. Isso é conseguido usando funções hash para gerar e armazenar hashes de senha:
hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366
Algoritmos de hash são funções unilaterais. Eles transformam qualquer quantidade de dados em uma "impressão digital" de comprimento fixo que não pode ser revertida. Eles também têm a propriedade de que, se a entrada mudar mesmo que seja um pouquinho, o hash resultante será completamente diferente (veja o exemplo acima). Isso é ótimo para proteger as senhas, porque queremos armazenar as senhas de uma forma que as proteja mesmo que o próprio arquivo de senhas esteja comprometido, mas, ao mesmo tempo, precisamos verificar se a senha de um usuário está correta.
Observação não relacionada: Nos velhos tempos da Internet, quando você clica no link 'esqueci minha senha', os sites enviam a você a sua senha em texto simples. Eles provavelmente os estavam armazenando em um banco de dados em algum lugar. Quando os hackers obtiveram acesso ao seu banco de dados, eles obteriam acesso a todas as senhas. Como muitos usuários usariam a mesma senha em vários sites, esse era um grande problema de segurança. Felizmente, hoje em dia, essa não é a prática comum.
Agora vem a pergunta: qual é a melhor maneira de armazenar senhas? Eu consideraria esta solução (serviço de autenticação e gerenciamento de usuários Stormpath) muito ideal:
Obviamente você não é o Google ou um banco, então esta é uma solução exagerada para você. Mas então vem a pergunta: quanta segurança seu projeto requer, quanto tempo e dinheiro você tem?
Para muitos aplicativos, embora não seja recomendado, armazenar uma senha embutida no código pode ser uma solução boa o suficiente. No entanto, adicionando facilmente algumas etapas extras de segurança da lista acima, você pode tornar seu aplicativo muito mais seguro.
Por exemplo, vamos supor que a etapa 1 não seja uma solução aceitável para o seu projeto. Você não quer que os usuários insiram a senha todas as vezes, ou nem mesmo quer / precisa que os usuários saibam a senha. Ainda assim, você tem informações confidenciais em algum lugar e deseja protegê-las. Você tem um aplicativo simples, não há servidor para armazenar seus arquivos ou isso é muito incômodo para o seu projeto. Seu aplicativo é executado em ambientes onde não é possível ter arquivos armazenados com segurança. Este é um dos piores casos, mas ainda com algumas medidas de segurança adicionais, você pode ter uma solução muito mais segura. Por exemplo, você pode armazenar as informações confidenciais em um arquivo e pode criptografar o arquivo. Você pode ter a chave privada de criptografia embutida no código. Você pode ofuscar o código, tornando um pouco mais difícil para alguém quebrá-lo.este link . (Quero avisá-lo mais uma vez que isso não é 100% seguro. Um hacker inteligente com o conhecimento e as ferramentas corretas pode hackear isso. Mas com base em seus requisitos e necessidades, essa pode ser uma solução boa o suficiente para você).
Esta pergunta mostra como armazenar senhas e outros dados em um arquivo criptografado: Java 256-bit AES Password-Based Encryption
MD5 é um algoritmo de hash, não um algoritmo de criptografia, em suma, você não pode obter de volta o que você hash, você só pode comparar. Idealmente, deve ser usado ao armazenar as informações de autenticação do usuário e não o nome de usuário e a senha do banco de dados. O nome de usuário do banco de dados e o pwd devem ser criptografados e mantidos em um arquivo de configuração, para fazer o mínimo.