Crittografare la password nei file di configurazione? [chiuso]


130

Ho un programma che legge le informazioni sul server da un file di configurazione e vorrei crittografare la password in quella configurazione che può essere letta dal mio programma e decifrata.

requirments:

  • Crittografa la password in chiaro da archiviare nel file
  • Decifrare la password crittografata letta dal file dal mio programma

Qualche consiglio su come farei per fare questo? Stavo pensando di scrivere il mio algoritmo, ma penso che sarebbe terribilmente insicuro.

Risposte:


172

Un modo semplice per farlo è utilizzare la crittografia basata su password in Java. Ciò consente di crittografare e decrittografare un testo utilizzando una password.

Ciò significa l'inizializzazione di una javax.crypto.Ciphercon l'algoritmo "AES/CBC/PKCS5Padding"e ottenere una chiave dalla javax.crypto.SecretKeyFactorycon l' "PBKDF2WithHmacSHA512"algoritmo.

Ecco un esempio di codice (aggiornato per sostituire la variante basata su MD5 meno sicura):

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);
    }
}

Rimane un problema: dove si dovrebbe archiviare la password che si utilizza per crittografare le password? Puoi memorizzarlo nel file sorgente e offuscarlo, ma non è troppo difficile ritrovarlo. In alternativa, è possibile assegnarlo come proprietà di sistema all'avvio del processo Java ( -DpropertyProtectionPassword=...).

Lo stesso problema persiste se si utilizza il KeyStore, anch'esso protetto da una password. Fondamentalmente, dovrai avere una password principale da qualche parte, ed è piuttosto difficile da proteggere.


3
Grazie per l'esempio di codice, è praticamente come ho finito per farlo. Per quanto riguarda la password che protegge le password in cui ho riscontrato lo stesso problema, ho seguito il metodo offuscato per ora, ma non ho ancora trovato una soluzione accettabile, grazie per i tuoi suggerimenti.
Petey B,

7
"In alternativa, puoi assegnarlo come proprietà di sistema all'avvio del processo Java (-DpropertyProtectionPassword = ...)". Notare che ciò renderebbe possibile estrarre la password usando "ps fax" su (GNU / Linux) / UNIX.
Ztyx,

7
@Ben È pratica comune codificare in Base64 per consentire di archiviare il valore risultante in un file di testo o in una colonna di database basata su stringhe o simili.
RB.

4
@ V.7 no. MD5 non è assolutamente sicuro per l'hash della password e non è mai stato progettato a tale scopo. Non usarlo mai per quello. In questi giorni, Argon2 è il migliore. Vedi owasp.org/index.php/Password_Storage_Cheat_Sheet e paragonie.com/blog/2016/02/how-safely-store-password-in-2016
Kimball Robinson

3
Molto meglio così. Naturalmente un sale casuale sicuro e un conteggio iterativo di un (conservatore, di fascia bassa) di 40 K sarebbero più belli, ma almeno hai indicato queste cose nei commenti e PBKDF2 e AES / CBC sono miglioramenti netti. Penso che sia fantastico il modo in cui hai gestito questo, aggiornando la risposta; Rimuoverò l'avvertimento. Votato il tuo commento in modo che le persone non siano sorprese di trovare il codice aggiornato (possono guardare le modifiche per trovare il vecchio codice suppongo). Potrebbe essere una buona idea ripulire anche i tuoi vecchi commenti.
Maarten Bodewes,

20

Sì, sicuramente non scrivere il tuo algoritmo. Java ha molte API di crittografia.

Se il sistema operativo su cui si sta installando ha un keystore, è possibile utilizzarlo per archiviare le chiavi crittografiche necessarie per crittografare e decrittografare i dati sensibili nella propria configurazione o in altri file.


4
+1 per l'utilizzo di un KeyStore! Non è altro che offuscamento se stai memorizzando la chiave nel file Jar.
Noines,

2
Se tutto ciò che serve è non avere la password memorizzata in chiaro, i keystore sono eccessivi.
Thorbjørn Ravn Andersen,

20

Dai un'occhiata a jasypt , che è una libreria che offre funzionalità di crittografia di base con il minimo sforzo.


16

Penso che l'approccio migliore sia quello di garantire che il tuo file di configurazione (contenente la tua password) sia accessibile solo a un account utente specifico . Ad esempio, potresti avere un utente specifico dell'applicazione appusera cui solo le persone fidate hanno la password (e a cui devono su).

In questo modo, non c'è fastidioso overhead di crittografia e hai ancora una password sicura.

EDIT: Sto assumendo che non stai esportando la configurazione dell'applicazione al di fuori di un ambiente attendibile (che non sono sicuro avrebbe senso, vista la domanda)


4

Bene per risolvere i problemi della password principale - l'approccio migliore non è quello di archiviare la password ovunque, l'applicazione dovrebbe crittografare le password per sé - in modo che solo lei possa decrittografarle. Quindi, se stavo usando un file .config, farei quanto segue, mySettings.config :

encryptTheseKeys = secretkey, anotherSecret

secretkey = unprotectedPasswordThatIputHere

anotherSecret = anotherPass

someKey = unprotectedSettingIdontCareAbout

quindi vorrei leggere le chiavi menzionate in encryptTheseKeys, applicare l'esempio dei Brodwalls dall'alto su di essi e riscriverli nel file con un marcatore di qualche tipo (diciamo crypt :) per far sapere all'applicazione di non farlo di nuovo, l'output sarebbe simile al seguente:

encryptTheseKeys = secretkey, anotherSecret

secretKey = crypt: ii4jfj304fjhfj934fouh938

anotherSecret = crypt: jd48jofh48h

someKey = unprotectedSettingIdontCareAbout

Assicurati solo di conservare gli originali nel tuo posto sicuro ...


2
Sì, è di 3 anni fa. Per evitare la chiave principale, ho finito per usare le chiavi RSA emesse dalla nostra CA interna. L'accesso alla chiave privata è protetto dalla crittografia con un'impronta digitale dell'hardware della macchina.
Petey B,

Vedo, sembra abbastanza solido. simpatico.
user1007231

@ user1007231 - Dove conservare - "Assicurati solo di conservare gli originali nel tuo posto sicuro ..."?
nanosoft,

@PeteyB - Non hai capito? Puoi indicarmi alcuni link che possono illuminarmi. Grazie
nanosoft

@nanosoft - Ottieni un "Aegis Secure Key USB" e conservalo in un documento di testo lì o sulla carta nel tuo portafoglio
user1007231

4

Il punto cruciale, e l'elefante nella stanza e tutto il resto, è che se la tua applicazione può ottenere la password, anche un hacker con accesso alla scatola può prenderla!

L'unico modo per aggirare questo problema è che l'applicazione richiede la "password principale" sulla console utilizzando lo standard input e quindi la utilizza per decrittografare le password archiviate nel file. Naturalmente, ciò rende impossibile avviare l'applicazione incustodita insieme al sistema operativo all'avvio.

Tuttavia, anche con questo livello di fastidio, se un hacker riesce ad ottenere l'accesso come root (o anche solo accedendo come l'utente che esegue la tua applicazione), potrebbe scaricare la memoria e trovare lì la password.

La cosa da garantire è di non consentire all'intera azienda di accedere al server di produzione (e quindi alle password) e assicurarsi che sia impossibile rompere questa scatola!


La vera soluzione è conservare la tua chiave privata altrove, come una carta o un HSM: en.wikipedia.org/wiki/Hardware_security_module
atom88



0

A seconda della sicurezza necessaria per i file di configurazione o della affidabilità dell'applicazione, http://activemq.apache.org/encrypted-passwords.html potrebbe essere una buona soluzione per te.

Se non si ha troppa paura della decodifica della password e può essere davvero semplice da configurare utilizzando un bean per memorizzare la chiave della password. Tuttavia, se hai bisogno di maggiore sicurezza, puoi impostare una variabile di ambiente con il segreto e rimuoverla dopo l'avvio. In questo modo devi preoccuparti che l'applicazione / il server non funzionino e che l'applicazione non si riavvii automaticamente.


usare un HSM è il modo ideale: en.wikipedia.org/wiki/Hardware_security_module
atom88

-8

Se si utilizza java 8, è possibile evitare l'uso dell'encoder e del decodificatore Base64 interni sostituendoli

return new BASE64Encoder().encode(bytes);

con

return Base64.getEncoder().encodeToString(bytes);

e

return new BASE64Decoder().decodeBuffer(property);

con

return Base64.getDecoder().decode(property);

Nota che questa soluzione non protegge i tuoi dati poiché i metodi per la decodifica sono memorizzati nello stesso posto. Rende solo più difficile da rompere. Principalmente evita di stamparlo e mostrarlo a tutti per errore.


26
Base64 non è la crittografia.
jwilleke,

1
Base64 non è una crittografia ed è l'esempio peggiore che puoi fornire ... molte persone credono che base64 sia un algoritmo di crittografia, quindi è meglio non confonderli ...
robob,

si noti che il metodo decode () nella parte superiore del file di origine esegue la crittografia effettiva. Tuttavia, questa codifica e decodifica base64 è necessaria per convertire da una stringa i byte al fine di passare questa funzione a qualcosa che può usare (un byte array byte [])
atom88
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.