Cifrar a senha nos ficheiros de configuração? [fechado]
Tenho um programa que lê as informações do servidor a partir de um ficheiro de configuração e gostaria de cifrar a senha nessa configuração que pode ser lida pelo meu programa e descodificada.
Requisitos:
- encriptar a senha de texto simples a guardar no ficheiro
- descodificar a senha encriptada lida do ficheiro do meu programa
10 answers
Uma forma simples de fazer isto é usar a encriptação baseada em senha em Java. Isto permite-lhe cifrar e descodificar um texto usando uma senha.
Isto basicamente significa inicializar um javax.crypto.Cipher
com o algoritmo "AES/CBC/PKCS5Padding"
e obter uma chave de javax.crypto.SecretKeyFactory
com o algoritmo "PBKDF2WithHmacSHA512"
.
Aqui está um exemplo de código (actualizado para substituir a variante menos segura baseada na MD5):
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class ProtectedConfigFile {
public static void main(String[] args) throws Exception {
String password = System.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("Run with -Dpassword=<password>");
}
// The salt (probably) can be stored along with the encrypted data
byte[] salt = new String("12345678").getBytes();
// Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers
int iterationCount = 40000;
// Other values give me java.security.InvalidKeyException: Illegal key size or default parameters
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(),
salt, iterationCount, keyLength);
String originalPassword = "secret";
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}
private static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}
private static String encrypt(String property, SecretKeySpec key) throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(property.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
private static String decrypt(String string, SecretKeySpec key) throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}
Resta um problema: onde deve guardar a senha que usa para cifrar as senhas? Você pode armazenar está no arquivo fonte e ofuscá-lo, mas não é muito difícil encontrá-lo novamente. Alternativamente, você pode dar-lhe como uma propriedade do sistema quando você iniciar o processo Java (-DpropertyProtectionPassword=...
).
O mesmo problema permanece se você usar o teclado, que também está protegido por uma senha. Basicamente, você vai precisar de ter uma senha mestre em algum lugar, e é muito difícil de proteger.
Se o SO em que está a instalar tiver um teclado, então poderá usá-lo para guardar as suas chaves de cifra que terá de cifrar e descodificar os dados sensíveis na sua configuração ou noutros ficheiros.
Confirajasypt , que é uma biblioteca que oferece capacidades básicas de encriptação com esforço mínimo.
Penso que a melhor abordagem é assegurar que o seu ficheiro de configuração (que contém a sua senha) seja apenas Acessível a uma conta de utilizador específica. Por exemplo, você pode ter um usuário específico da aplicação appuser
para o qual apenas as pessoas de confiança têm a senha (e para a qual su
têm).
Editar: presumo que não esteja a exportar a sua configuração da aplicação fora de um ambiente de confiança (o que Não tenho certeza faria qualquer sentido, dada a questão)
Bem, para resolver os problemas da senha mestre-a melhor abordagem é não armazenar a senha em qualquer lugar, a aplicação deve criptografar senhas para si mesma - de modo que só ele pode descriptografá-las. Então, se eu estivesse a usar um .config file eu faria o seguinte, mySettings.configuração:
Então eu leria nas chaves que são mencionadas no encriptar as teclas, aplicar o exemplo das 'brochuras' a partir de cima sobre elas e escreva-os de volta para o ficheiro com um marcador de algum tipo (vamos dizer crypt: ) para que a aplicação saiba que não o fará de novo, o resultado ficaria assim:Encriptar os Keys=secretKey, outro segredo
SecretKey=unprotected sasswordthather
Outro segredo=outro saco
SomeKey=unprotectedSettingIdontCareAbout
Certifica-te apenas de manter os originais no teu próprio lugar seguro...Encriptar os Keys=secretKey, outro segredo
SecretKey= crypt: ii4jfj304fjhfj934fouh938
Outro segredo= cripta: jd48jofh48h
SomeKey=unprotectedSettingIdontCareAbout
A única maneira de contornar isto, é que a aplicação pede a "senha mestre" na consola, usando a entrada padrão, e então usa isto para descodificar as senhas armazenadas no ficheiro. É claro que isso torna completamente impossível ter o início da aplicação sem supervisão, juntamente com o SO quando ele Botas.
No entanto, mesmo com este nível de aborrecimento, se um hacker consegue obter acesso root (ou mesmo apenas acesso como o utilizador que executa a sua aplicação), ele pode descarregar a memória e encontrar a senha lá.
A coisa a garantir, é não deixar toda a empresa ter acesso ao servidor de produção (e, assim, às senhas), e certifique-se de que é impossível quebrar esta caixa!
Tenta usar os métodos de encriptação do ESAPIs. É fácil de configurar e você também pode facilmente mudar suas chaves.
Http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
Tu
1)cifrar 2)descodificar 3) sinal 4)sinal 5)amarração 6) assinaturas baseadas no tempo e muito mais com apenas uma biblioteca.
Veja o que está disponível no Jetty para armazenar senha (ou traços) em arquivos de configuração, e considere se a codificação OBF pode ser útil para você. Então veja na fonte como é feito.
Http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
Dependendo da segurança dos ficheiros de configuração ou da fiabilidade da sua aplicação, http://activemq.apache.org/encrypted-passwords.html pode ser uma boa solução para ti.
Se você não tiver muito medo da senha ser decifrada e pode ser muito simples configurar usando um feijão para armazenar a chave de senha. No entanto, se você precisar de mais segurança, você pode definir uma variável de ambiente com o segredo e removê-lo após o lançamento. Com isso você tem que se preocupar sobre a aplicação / servidor a descer e não a aplicação não a relançar automaticamente.
Se estiver a utilizar java 8 , a utilização do codificador e descodificador interno Base64 pode ser evitada substituindo
return new BASE64Encoder().encode(bytes);
Com
return Base64.getEncoder().encodeToString(bytes);
E
return new BASE64Decoder().decodeBuffer(property);
Com
return Base64.getDecoder().decode(property);
Note que esta solução não protege os seus dados, uma vez que os métodos de descodificação são armazenados no mesmo local. Só torna mais difícil partir. Principalmente evita imprimi-lo e mostrá-lo a todos por engano.