Yapılandırma Dosyalarında Parola Şifrelensin mi? [kapalı]


130

Bir yapılandırma dosyasından sunucu bilgilerini okuyan ve programım tarafından okunabilen ve şifresi çözülebilen bu yapılandırmadaki parolayı şifrelemek isteyen bir programım var.

İhtiyaçlara:

  • Dosyada saklanacak düz metin parolayı şifreleyin
  • Programımdan dosyadan okunan şifreli parolanın şifresini çöz

Bunu nasıl yapacağımla ilgili herhangi bir öneriniz var mı? Kendi algoritmamı yazmayı düşünüyordum ama son derece güvensiz olacağını hissediyorum.

Yanıtlar:


172

Bunu yapmanın basit bir yolu, Java'da Parola Tabanlı Şifreleme kullanmaktır. Bu, bir parola kullanarak bir metni şifrelemenize ve şifresini çözmenize olanak tanır.

Bu temelde bir başlatılıyor demektir javax.crypto.Cipheralgoritması ile "AES/CBC/PKCS5Padding"ve bir anahtarı alma javax.crypto.SecretKeyFactoryile "PBKDF2WithHmacSHA512"algoritma.

Aşağıda bir kod örneği verilmiştir (daha az güvenli MD5 tabanlı varyantın yerini alacak şekilde güncellendi):

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

Geriye bir sorun kaldı: Parolaları şifrelemek için kullandığınız parolayı nerede saklamalısınız? Bunu kaynak dosyada saklayabilir ve gizleyebilirsiniz, ancak onu tekrar bulmak çok zor değildir. Alternatif olarak, Java işlemini başlattığınızda bunu bir sistem özelliği olarak verebilirsiniz ( -DpropertyProtectionPassword=...).

Aynı sorun bir parola ile korunan KeyStore'u kullanırsanız da devam eder. Temel olarak, bir yerde bir ana şifreniz olması gerekecek ve bunu korumak oldukça zordur.


3
Kod örneği için teşekkürler, hemen hemen bunu nasıl yaptım. Aynı problemle karşılaştığım şifreleri koruyan şifre ile ilgili olarak, şimdilik obfuscate it yöntemini bıraktım ancak henüz kabul edilebilir bir çözüm bulamadım, önerileriniz için teşekkürler.
Petey B

7
"Alternatif olarak, Java işlemini başlattığınızda bunu bir sistem özelliği olarak verebilirsiniz (-DpropertyProtectionPassword = ...)". Bunun, (GNU / Linux) / UNIX üzerinde "ps faks" kullanarak parolayı çıkarmayı mümkün kıldığını unutmayın.
Ztyx

7
@Ben Ortaya çıkan değeri bir metin dosyasında veya dize tabanlı bir veritabanı sütununda veya benzerinde depolamanıza izin vermek için Base64'e kodlamak yaygın bir uygulamadır.
RB.

4
@ V.7 hayır. MD5, parola bozma için kesinlikle güvenli değildir ve hiçbir zaman bu amaç için tasarlanmamıştır. Asla bunun için kullanmayın. Bugünlerde Argon2 en iyisidir. Bkz owasp.org/index.php/Password_Storage_Cheat_Sheet ve paragonie.com/blog/2016/02/how-safely-store-password-in-2016
Kimball Robinson

3
Böylesi çok daha iyi. Elbette güvenli bir rastgele tuz ve 40K'lık (muhafazakar, düşük uç) bir yineleme sayısı daha iyi olurdu, ancak en azından bunları yorumlarda belirtmişsiniz ve PBKDF2 ve AES / CBC kesin iyileştirmelerdir. Cevabı güncelleyerek bunu nasıl hallettiğinizin harika olduğunu düşünüyorum; Uyarıyı kaldıracağım. İnsanların güncellenmiş kodu bulmalarına şaşırmaması için yorumunuzu oyladılar (sanırım eski kodu bulmak için düzenlemelere bakabilirler). Eski yorumlarınızı da temizlemek iyi bir fikir olabilir.
Maarten Bodewes

20

Evet, kesinlikle kendi algoritmanızı yazmayın. Java'da çok sayıda kriptografi API'si vardır.

Yüklediğiniz işletim sisteminin bir anahtar deposu varsa, yapılandırmanızdaki veya diğer dosyalardaki hassas verileri şifrelemek ve şifresini çözmek için ihtiyaç duyacağınız şifreleme anahtarlarınızı saklamak için bunu kullanabilirsiniz.


4
Bir KeyStore kullanmak için +1! Anahtarı Jar dosyasında saklıyorsanız, gizlemeden başka bir şey değildir.
ninesided

2
Gerekli olan tek şey parolanın açık metin olarak saklanması değilse, o zaman anahtar depoları gereğinden fazla olur.
Thorbjørn Ravn Andersen


16

Bence en iyi yaklaşım, yapılandırma dosyanıza (parolanızı içeren) yalnızca belirli bir kullanıcı hesabı tarafından erişilebilir olmasını sağlamaktır . Örneğin, appuseryalnızca güvenilir kişilerin şifresine sahip olduğu (ve sahip oldukları su) uygulamaya özel bir kullanıcıya sahip olabilirsiniz .

Bu şekilde, can sıkıcı bir şifreleme yükü yoktur ve yine de güvenli bir şifreniz vardır.

DÜZENLEME: Uygulama yapılandırmanızı güvenilir bir ortamın dışına aktarmadığınızı varsayıyorum (soru göz önüne alındığında herhangi bir anlam ifade edeceğinden emin değilim)


4

Ana parola sorunlarını çözmek için - en iyi yaklaşım parolayı herhangi bir yerde saklamamaktır, uygulama parolaları kendisi için şifrelemelidir - böylece yalnızca onların şifresini çözebilir. Yani bir .config dosyası kullanıyor olsaydım, aşağıdakileri yapardım , mySettings.config :

encryptTheseKeys = SecretKey, anotherSecret

SecretKey = unprotectedPasswordThatIputHere

anotherSecret = anotherPass

someKey = unprotectedSettingIdontCareAbout

bu yüzden, encryptTheseKeys'de belirtilen anahtarları okurdum, Brodwalls örneğini yukarıdan uygulayıp, uygulamaya bunu yapmamasını bildirmek için bir tür işaretçi ile ( crypt diyelim ) dosyaya geri yazarım . yine çıktı şöyle görünecektir:

encryptTheseKeys = SecretKey, anotherSecret

secretKey = crypt: ii4jfj304fjhfj934fouh938

anotherSecret = crypt: jd48jofh48h

someKey = unprotectedSettingIdontCareAbout

Orijinalleri kendi güvenli yerde sakladığınızdan emin olun ...


2
Evet, bu 3 yıl öncesine ait. Ana anahtardan kaçınmak için dahili CA'mızdan verilen RSA anahtarlarını kullandım. Özel anahtara erişim, makine donanımının parmak iziyle şifrelenerek korunur.
Petey B

Anlıyorum, kulağa oldukça sağlam geliyor. Güzel.
user1007231

@ user1007231 - Nerede saklanmalı - "Orijinalleri kendi güvenli yerinizde sakladığınızdan emin olun ..."?
nanosoft

@PeteyB - Anlamadın mı? Beni aydınlatabilecek bazı bağlantılara işaret edebilir misin? Teşekkürler
nanosoft

@nanosoft - Bir "Aegis Secure Key USB" alın ve orada bir metin belgesinde veya cüzdanınızda kağıt üzerinde
saklayın

4

Önemli olan, odadaki fil ve tüm bunlar, uygulamanız şifreyi ele geçirebilirse, kutuya erişimi olan bir bilgisayar korsanı da onu ele geçirebilir!

Bunun etrafından dolaşmanın tek yolu, uygulamanın Standart Giriş'i kullanarak konsolda "ana şifre" sorması ve ardından bunu dosyada depolanan şifrelerin şifresini çözmek için kullanmasıdır. Elbette bu, uygulamanın önyüklendiğinde işletim sistemi ile birlikte gözetimsiz olarak başlatılmasını tamamen imkansız hale getirir.

Bununla birlikte, bu düzeyde bir rahatsızlıkla bile, bir bilgisayar korsanı root erişimini (hatta uygulamanızı çalıştıran kullanıcı olarak erişmeyi) başarırsa, hafızayı boşaltabilir ve şifreyi orada bulabilir.

Sağlanması gereken şey, tüm şirketin üretim sunucusuna (ve dolayısıyla şifrelere) erişmesine izin vermemek ve bu kutuyu kırmanın imkansız olduğundan emin olmaktır!


Gerçek çözüm, özel anahtarınızı bir kart veya HSM gibi başka bir yerde saklamaktır: en.wikipedia.org/wiki/Hardware_security_module
atom88



0

Yapılandırma dosyalarına ne kadar güvenli ihtiyacınız olduğuna veya uygulamanızın ne kadar güvenilir olduğuna bağlı olarak, http://activemq.apache.org/encrypted-passwords.html sizin için iyi bir çözüm olabilir.

Şifrenin çözülmesinden çok korkmuyorsanız ve şifre anahtarını saklamak için bir fasulye kullanarak yapılandırmak gerçekten basit olabilir. Bununla birlikte, daha fazla güvenliğe ihtiyacınız varsa, gizli bilgiyle bir ortam değişkeni ayarlayabilir ve başlattıktan sonra kaldırabilirsiniz. Bununla birlikte, uygulamanın / sunucunun kapanması ve uygulamanın otomatik olarak yeniden başlatılmaması konusunda endişelenmeniz gerekir.



-8

Eğer kullanıyorsanız java 8 dahili Base64 kodlayıcı ve kod çözücü kullanımının değiştirilmesi önlenebilir

return new BASE64Encoder().encode(bytes);

ile

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

ve

return new BASE64Decoder().decodeBuffer(property);

ile

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

Şifre çözme yöntemleri aynı yerde saklandığından, bu çözümün verilerinizi korumadığını unutmayın. Sadece kırılmayı daha da zorlaştırıyor. Esas olarak, yazdırmaktan ve yanlışlıkla herkese göstermekten kaçınır.


26
Base64, şifreleme değildir.
jwilleke

1
Base64 ancryption değil ve sağlayabileceğiniz en kötü örnek ... bir çok insan
base64'ün

kaynak dosyanın üstündeki decode () yönteminin gerçek şifrelemeyi yaptığına dikkat edin. Ancak, bu base64 kodlama ve kod çözme, bu işlevi kullanabileceği bir şeyi (bir bayt dizisi baytı []) geçirmek için bir dizeden baytları dönüştürmek için gereklidir (bir bayt dizisi baytı [])
atom88
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.