Nunca codifique senhas em seu código. Isso foi mencionado recentemente no Top 25 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 inevitavelmente for conhecida. E porque é codificado, é uma dor enorme para consertar.
Você deve armazenar informações de configuração, incluindo senhas, em um arquivo separado que o aplicativo lê quando é iniciado. Essa é a única maneira real de evitar que a senha vaze como resultado da descompilação (nunca a compile no binário para começar).
Para obter mais informações sobre esse erro comum, leia 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 foi projetado para armazenar todos os tipos de configurações do programa, algumas das quais podem incluir um nome de usuário e 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
após mostrar uma caixa de diálogo solicitando o nome de usuário e a senha. Quando você precisar se conectar ao banco de dados, basta usar o getUsername
e getPassword
métodos 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 apropriadas para impedir que usuários não autorizados visualizem os arquivos brutos (permissões do UNIX, permissões do Windows etc.). No Linux, pelo menos, isso não é um problema, porque chamar
Preferences.userNodeForPackage
criará o arquivo XML no diretório inicial do usuário atual, que não pode ser lido por outros usuários de qualquer maneira. No Windows, a situação pode ser diferente. Observações mais importantes: Houve muita discussão nos comentários desta resposta e de outros sobre qual é a arquitetura correta para essa situação. A pergunta original realmente não menciona o contexto em que o aplicativo está sendo usado, então falarei sobre as duas situações em que posso pensar. O primeiro é o caso em que a pessoa que usa o programa já conhece (e está autorizada a conhecer) 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 conhecer as credenciais de login do banco de dados
Nesse caso, a solução que mencionei acima funcionará. A
Preference
Java class 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á conhecia as credenciais. Segundo caso:tentando 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 conhecer 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 ele será o proprietário. Na verdade, não há realmente nenhuma boa maneira de usar esse caso de maneira segura.
Caso correto:usando uma arquitetura multicamada
A maneira correta de fazer isso é ter uma camada intermediária, entre o servidor de banco de dados e o aplicativo cliente, que autentique usuários individuais e permita 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 o 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 nenhum 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 é:
- O cliente é autenticado com a camada de lógica de negócios usando o nome de usuário/senha pessoal do usuário. O nome de usuário e a senha são conhecidos pelo usuário e não estão relacionados às credenciais de login do banco de dados de forma alguma.
- Se a autenticação for bem-sucedida, o cliente fará uma solicitação à camada de lógica de negócios solicitando algumas informações do banco de dados. Por exemplo, um inventário de produtos. Observe que a solicitação do cliente não é uma consulta SQL; é uma chamada de procedimento remoto como
getInventoryList
. - A camada de lógica de negócios se conecta ao banco de dados e recupera as informações solicitadas. A camada lógica de negócios é responsável por formar uma consulta SQL segura com base na solicitação do usuário. Quaisquer parâmetros para a consulta SQL devem ser higienizados para evitar ataques de injeção de SQL.
- A camada de lógica de negócios envia a lista de inventário de volta ao aplicativo cliente.
- O cliente exibe a lista de inventário para o usuário.
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.