Crypter le mot de passe dans les fichiers de configuration? [fermé]

130

J'ai un programme qui lit les informations du serveur à partir d'un fichier de configuration et je voudrais crypter le mot de passe dans cette configuration qui peut être lu par mon programme et décrypté.

Exigences:

  • Crypter le mot de passe en texte brut à stocker dans le fichier
  • Décrypter le mot de passe crypté lu à partir du fichier de mon programme

Des recommandations sur la manière de procéder? Je pensais écrire mon propre algorithme mais je pense que ce serait terriblement dangereux.

Petey B
la source

Réponses:

172

Un moyen simple de le faire est d'utiliser le cryptage basé sur le mot de passe en Java. Cela vous permet de crypter et de décrypter un texte à l'aide d'un mot de passe.

Cela signifie essentiellement initialiser un javax.crypto.Cipheralgorithme with "AES/CBC/PKCS5Padding"et obtenir une clé javax.crypto.SecretKeyFactoryavec l' "PBKDF2WithHmacSHA512"algorithme.

Voici un exemple de code (mis à jour pour remplacer la variante la moins sécurisée basée sur 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);
    }
}

Un problème demeure: où devez-vous stocker le mot de passe que vous utilisez pour crypter les mots de passe? Vous pouvez le stocker dans le fichier source et le masquer, mais il n'est pas trop difficile de le retrouver. Vous pouvez également le donner en tant que propriété système lorsque vous démarrez le processus Java ( -DpropertyProtectionPassword=...).

Le même problème persiste si vous utilisez le KeyStore, qui est également protégé par un mot de passe. Fondamentalement, vous aurez besoin d'un mot de passe principal quelque part, et c'est assez difficile à protéger.

Johannes Brodwall
la source
3
Merci pour l'exemple de code, c'est à peu près comment j'ai fini par le faire. En ce qui concerne le mot de passe qui protège les mots de passe, j'ai rencontré le même problème, je suis allé avec la méthode obscurcir pour l'instant mais je n'ai pas encore trouvé de solution acceptable, merci pour vos suggestions.
Petey B
7
"Alternativement, vous pouvez le donner comme propriété système lorsque vous démarrez le processus Java (-DpropertyProtectionPassword = ...)". Notez que cela permettrait d'extraire le mot de passe en utilisant "ps fax" sous (GNU / Linux) / UNIX.
Ztyx
7
@Ben Il est courant d'encoder en Base64 pour vous permettre de stocker la valeur résultante dans un fichier texte ou une colonne de base de données basée sur une chaîne, ou similaire.
RB.
4
@ V.7 non. MD5 n'est absolument pas sécurisé pour le hachage de mot de passe et n'a jamais été conçu à cette fin. Ne l'utilisez jamais pour ça. De nos jours, Argon2 est le meilleur. Voir owasp.org/index.php/Password_Storage_Cheat_Sheet et paragonie.com/blog/2016/02/how-safely-store-password-in-2016
Kimball Robinson
3
Beaucoup mieux de cette façon. Bien sûr, un sel aléatoire sécurisé et un nombre d'itérations d'un (conservateur, bas de gamme) de 40K serait plus agréable, mais au moins vous avez indiqué ces choses dans les commentaires et PBKDF2 et AES / CBC sont des améliorations définitives. Je pense que c'est formidable comment vous avez géré cela, en mettant à jour la réponse; Je vais supprimer l'avertissement. A voté votre commentaire afin que les gens ne soient pas surpris de trouver le code mis à jour (ils peuvent regarder les modifications pour trouver l'ancien code, je suppose). Cela pourrait également être une bonne idée de nettoyer vos anciens commentaires.
Maarten Bodewes
20

Oui, n'écrivez certainement pas votre propre algorithme. Java possède de nombreuses API de cryptographie.

Si le système d'exploitation sur lequel vous installez dispose d'un fichier de clés, vous pouvez l'utiliser pour stocker vos clés de chiffrement dont vous aurez besoin pour chiffrer et déchiffrer les données sensibles de votre configuration ou d'autres fichiers.

JeeBee
la source
4
+1 pour l'utilisation d'un KeyStore! Ce n'est rien de plus qu'une obfuscation si vous stockez la clé dans le fichier Jar.
ninesided
2
Si tout ce qui est nécessaire est de ne pas stocker le mot de passe en texte clair, les fichiers de clés sont exagérés.
Thorbjørn Ravn Andersen
20

Découvrez jasypt , une bibliothèque offrant des capacités de cryptage de base avec un minimum d'effort.

Kaitsu
la source
16

Je pense que la meilleure approche est de s'assurer que votre fichier de configuration (contenant votre mot de passe) n'est accessible qu'à un compte utilisateur spécifique . Par exemple, vous pouvez avoir un utilisateur spécifique appuserà une application dont seules les personnes de confiance ont le mot de passe (et auquel elles sudoivent).

De cette façon, il n'y a pas de frais généraux de cryptographie ennuyeux et vous avez toujours un mot de passe sécurisé.

EDIT: Je suppose que vous n'exportez pas la configuration de votre application en dehors d'un environnement de confiance (ce qui, je ne suis pas sûr, aurait du sens, étant donné la question)

oxbow_lakes
la source
4

Eh bien, pour résoudre les problèmes de mot de passe principal - la meilleure approche est de ne pas stocker le mot de passe n'importe où, l'application doit chiffrer les mots de passe pour elle-même - afin qu'elle seule puisse les déchiffrer. Donc, si j'utilisais un fichier .config, je ferais ce qui suit, mySettings.config :

encryptTheseKeys = secretKey, un autreSecret

secretKey = non protégéPasswordThatIputHere

anotherSecret = anotherPass

someKey = non protégéSettingIdontCareAbout

donc je lirais les clés mentionnées dans encryptTheseKeys, appliquerais l'exemple Brodwalls ci-dessus et les réécrirais dans le fichier avec un marqueur d'une sorte (disons crypt :) pour que l'application sache de ne pas le faire encore une fois, la sortie ressemblerait à ceci:

encryptTheseKeys = secretKey, un autreSecret

secretKey = crypt: ii4jfj304fjhfj934fouh938

anotherSecret = crypt: jd48jofh48h

someKey = non protégéSettingIdontCareAbout

Assurez-vous simplement de conserver les originaux dans votre propre endroit sécurisé ...

user1007231
la source
2
Ouais, ça date d'il y a 3 ans. Pour éviter la clé principale, j'ai fini par utiliser des clés RSA émises par notre CA interne. L'accès à la clé privée est protégé en étant crypté avec une empreinte digitale du matériel de la machine.
Petey B
Je vois, ça sonne assez solide. agréable.
user1007231
@ user1007231 - Où conserver - "Assurez-vous simplement de conserver les originaux dans votre propre endroit sécurisé ..."?
nanosoft
@PeteyB - Vous n'avez pas compris? Pouvez-vous m'indiquer quelques liens qui peuvent m'éclairer. Merci
nanosoft
@nanosoft - Obtenez une clé USB "Aegis Secure Key" et stockez-la dans un document texte ou sur papier dans votre portefeuille
user1007231
4

Le gros point, et l'éléphant dans la salle et tout ça, c'est que si votre application peut obtenir le mot de passe, alors un hacker ayant accès à la boîte peut aussi s'en emparer!

Le seul moyen de contourner ce problème est que l'application demande le "mot de passe principal" sur la console en utilisant l'entrée standard, puis l'utilise pour déchiffrer les mots de passe stockés dans le fichier. Bien sûr, cela rend complètement impossible le démarrage de l'application sans surveillance avec le système d'exploitation au démarrage.

Cependant, même avec ce niveau de gêne, si un pirate parvient à obtenir un accès root (ou même simplement un accès en tant qu'utilisateur exécutant votre application), il pourrait vider la mémoire et y trouver le mot de passe.

La chose à s'assurer, c'est de ne pas laisser toute l'entreprise avoir accès au serveur de production (et donc aux mots de passe), et de s'assurer qu'il est impossible de casser cette boîte!

Stolsvik
la source
La vraie solution est de stocker votre clé privée ailleurs, comme une carte ou un HSM: en.wikipedia.org/wiki/Hardware_security_module
atom88
0

Selon la sécurité dont vous avez besoin des fichiers de configuration ou la fiabilité de votre application, http://activemq.apache.org/encrypted-passwords.html peut être une bonne solution pour vous.

Si vous n'avez pas trop peur du déchiffrement du mot de passe et que cela peut être très simple à configurer en utilisant un bean pour stocker la clé de mot de passe. Cependant, si vous avez besoin de plus de sécurité, vous pouvez définir une variable d'environnement avec le secret et la supprimer après le lancement. Avec cela, vous devez vous soucier de la panne de l'application / du serveur et non du redémarrage automatique de l'application.

CPrescott
la source
utiliser un HSM est le moyen idéal: en.wikipedia.org/wiki/Hardware_security_module
atom88
-8

Si vous utilisez java 8, l'utilisation de l'encodeur et du décodeur internes Base64 peut être évitée en remplaçant

return new BASE64Encoder().encode(bytes);

avec

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

et

return new BASE64Decoder().decodeBuffer(property);

avec

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

Notez que cette solution ne protège pas vos données car les méthodes de décryptage sont stockées au même endroit. Cela rend simplement plus difficile la rupture. Principalement, cela évite de l'imprimer et de le montrer à tout le monde par erreur.

Antonio Raposo
la source
26
Base64 n'est pas un cryptage.
jwilleke
1
Base64 n'est pas un cryptage et c'est le pire exemple que vous puissiez fournir ... beaucoup de gens pensent que base64 est un algorithme de cryptage, alors mieux vaut ne pas les confondre ...
robob
notez que la méthode decode () en haut du fichier source effectue le cryptage réel. Cependant, cet encodage et décodage base64 est nécessaire pour convertir à partir d'une chaîne les octets afin de transmettre à cette fonction quelque chose qu'il peut utiliser (un octet de tableau d'octets [])
atom88