Enkripsi Kata Sandi dalam File Konfigurasi? [Tutup]


130

Saya memiliki program yang membaca informasi server dari file konfigurasi dan ingin mengenkripsi kata sandi dalam konfigurasi yang dapat dibaca oleh program saya dan didekripsi.

Persyaratan:

  • Enkripsi kata sandi plaintext untuk disimpan dalam file
  • Dekripsi kata sandi terenkripsi yang dibaca dari file dari program saya

Adakah rekomendasi tentang cara melakukan ini? Saya sedang berpikir untuk menulis algoritma saya sendiri tetapi saya merasa itu akan sangat tidak aman.

Jawaban:


172

Cara sederhana untuk melakukan ini adalah dengan menggunakan Enkripsi Berbasis Kata Sandi di Java. Ini memungkinkan Anda untuk mengenkripsi dan mendekripsi teks dengan menggunakan kata sandi.

Ini pada dasarnya berarti menginisialisasi javax.crypto.Cipherdengan algoritma "AES/CBC/PKCS5Padding"dan mendapatkan kunci dari javax.crypto.SecretKeyFactorydengan "PBKDF2WithHmacSHA512"algoritma.

Ini adalah contoh kode (diperbarui untuk mengganti varian berbasis MD5 yang kurang aman):

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

Masih ada satu masalah: Di mana Anda harus menyimpan kata sandi yang Anda gunakan untuk mengenkripsi kata sandi? Anda dapat menyimpannya di file sumber dan mengaburkannya, tetapi tidak terlalu sulit untuk menemukannya lagi. Atau, Anda bisa memberikannya sebagai properti sistem ketika Anda memulai proses Java ( -DpropertyProtectionPassword=...).

Masalah yang sama tetap ada jika Anda menggunakan KeyStore, yang juga dilindungi oleh kata sandi. Pada dasarnya, Anda perlu memiliki satu kata sandi utama di suatu tempat, dan itu cukup sulit untuk dilindungi.


3
Terima kasih untuk contoh kode, cukup banyak bagaimana saya akhirnya melakukannya. Berkenaan dengan kata sandi yang melindungi kata sandi yang saya temukan pada masalah yang sama, saya pergi dengan metode mengaburkan untuk saat ini tetapi belum datang dengan solusi yang dapat diterima, terima kasih atas saran Anda.
Petey B

7
"Atau, Anda bisa memberikannya sebagai properti sistem ketika Anda memulai proses Java (-DpropertyProtectionPassword = ...)". Perhatikan bahwa ini akan memungkinkan untuk mengekstrak kata sandi menggunakan "ps fax" pada (GNU / Linux) / UNIX.
Ztyx

7
@Ben Ini adalah praktik umum untuk menyandikan ke Base64 untuk memungkinkan Anda menyimpan nilai yang dihasilkan dalam file teks atau kolom basis data berbasis string, atau serupa.
RB.

4
@ V.7 tidak. MD5 sama sekali tidak aman untuk hashing kata sandi dan tidak pernah dirancang untuk tujuan itu. Jangan pernah menggunakannya untuk itu. Saat ini, Argon2 adalah yang terbaik. Lihat owasp.org/index.php/Password_Storage_Cheat_Sheet dan paragonie.com/blog/2016/02/how-safely-store-password-in-2016
Kimball Robinson

3
Jauh lebih baik seperti ini. Tentu saja garam acak yang aman dan jumlah iterasi 40K (konservatif, low-end) akan lebih baik, tetapi setidaknya Anda telah mengindikasikan hal-hal ini di komentar dan PBKDF2 dan AES / CBC merupakan perbaikan yang pasti. Saya pikir sangat bagus bagaimana Anda menangani ini, dengan memperbarui jawabannya; Saya akan menghapus peringatan. Dipilih komentar Anda sehingga orang tidak terkejut menemukan kode yang diperbarui (mereka dapat melihat suntingan untuk menemukan kode lama saya kira). Mungkin ide yang baik untuk membersihkan komentar lama Anda juga.
Maarten Bodewes

20

Ya, pasti jangan menulis algoritma Anda sendiri. Java memiliki banyak API kriptografi.

Jika OS yang Anda instal memiliki keystore, maka Anda dapat menggunakannya untuk menyimpan kunci kripto yang Anda perlukan untuk mengenkripsi dan mendekripsi data sensitif dalam konfigurasi Anda atau file lain.


4
+1 untuk menggunakan KeyStore! Tidak lebih dari kebingungan jika Anda menyimpan kunci dalam file Jar.
ninesided

2
Jika semua yang diperlukan bukan untuk membuat kata sandi disimpan dalam teks yang jelas, maka keystores adalah berlebihan.
Thorbjørn Ravn Andersen

20

Lihatlah jasypt , yang merupakan perpustakaan yang menawarkan kemampuan enkripsi dasar dengan upaya minimal.


16

Saya pikir pendekatan terbaik adalah memastikan bahwa file konfigurasi Anda (berisi kata sandi Anda) hanya dapat diakses oleh akun pengguna tertentu . Misalnya, Anda mungkin memiliki pengguna khusus aplikasi appuseryang hanya orang tepercaya yang memiliki kata sandi (dan yang mereka sugunakan).

Dengan begitu, tidak ada overhead kriptografi yang mengganggu dan Anda masih memiliki kata sandi yang aman.

EDIT: Saya berasumsi bahwa Anda tidak mengekspor konfigurasi aplikasi Anda di luar lingkungan tepercaya (yang saya tidak yakin akan masuk akal, mengingat pertanyaan)


4

Nah untuk menyelesaikan masalah kata sandi master - pendekatan terbaik adalah tidak menyimpan kata sandi di mana saja, aplikasi harus mengenkripsi kata sandi itu sendiri - sehingga hanya itu yang dapat mendekripsi kata sandi tersebut. Jadi jika saya menggunakan file .config saya akan melakukan yang berikut, mySettings.config :

encryptTheseKeys = secretKey, anotherSecret

secretKey = unsProtectedPasswordThatIputHere

anotherSecret = anotherPass

someKey = unsotectedSettingIdontCareAbout

jadi saya akan membaca di kunci yang disebutkan dalam mengenkripsi TheseKeys, menerapkan contoh Brodwalls dari atas pada mereka dan menulisnya kembali ke file dengan penanda semacam (misalkan crypt:) untuk membiarkan aplikasi tahu untuk tidak melakukannya lagi, hasilnya akan terlihat seperti ini:

encryptTheseKeys = secretKey, anotherSecret

secretKey = crypt: ii4jfj304fjhfj934fouh938

anotherSecret = crypt: jd48jofh48h

someKey = unsotectedSettingIdontCareAbout

Pastikan untuk menyimpan aslinya di tempat aman Anda sendiri ...


2
Ya, ini dari 3 tahun yang lalu. Untuk menghindari kunci master, saya akhirnya menggunakan kunci RSA yang dikeluarkan dari CA internal kami. Akses ke kunci pribadi dilindungi dengan dienkripsi dengan sidik jari perangkat keras mesin.
Petey B

Begitu ya, terdengar cukup solid. bagus.
user1007231

@ user1007231 - Tempat menyimpan - "Pastikan untuk menyimpan dokumen asli di tempat Anda sendiri yang aman ..."?
nanosoft

@ PeteyB - Tidak mengerti? Bisakah Anda mengarahkan saya ke beberapa tautan yang dapat mencerahkan saya. Terima kasih
nanosoft

@nanosoft - Dapatkan "Aegis Secure Key USB" dan simpan dalam dokumen teks di sana atau di atas kertas di dompet Anda
user1007231

4

Intinya, dan gajah di ruangan itu dan semua itu, adalah bahwa jika aplikasi Anda dapat memperoleh kata sandi, maka seorang peretas yang memiliki akses ke kotak itu juga bisa mendapatkannya!

Satu-satunya cara untuk mengatasi hal ini, adalah bahwa aplikasi meminta "kata sandi utama" pada konsol menggunakan Input Standar, dan kemudian menggunakannya untuk mendekripsi kata sandi yang disimpan dalam file. Tentu saja, ini benar-benar membuat tidak mungkin untuk memiliki aplikasi memulai tanpa pengawasan bersama dengan OS saat boot.

Namun, bahkan dengan tingkat gangguan ini, jika seorang hacker berhasil mendapatkan akses root (atau bahkan hanya mengakses sebagai pengguna yang menjalankan aplikasi Anda), ia dapat membuang memori dan menemukan kata sandi di sana.

Hal yang perlu dipastikan, adalah untuk tidak membiarkan seluruh perusahaan memiliki akses ke server produksi (dan dengan demikian ke kata sandi), dan pastikan bahwa tidak mungkin untuk memecahkan kotak ini!


Solusi sebenarnya adalah menyimpan kunci pribadi Anda di tempat lain, seperti kartu atau HSM: en.wikipedia.org/wiki/Hardware_security_module
atom88



0

Bergantung pada seberapa aman Anda memerlukan file konfigurasi atau seberapa andal aplikasi Anda, http://activemq.apache.org/encrypted-passwords.html mungkin merupakan solusi yang baik untuk Anda.

Jika Anda tidak terlalu takut kata sandi didekripsi dan sangat mudah untuk mengkonfigurasi menggunakan kacang untuk menyimpan kunci kata sandi. Namun, jika Anda membutuhkan lebih banyak keamanan Anda dapat mengatur variabel lingkungan dengan rahasia dan menghapusnya setelah diluncurkan. Dengan ini Anda harus khawatir tentang aplikasi / server turun dan bukan aplikasi tidak secara otomatis meluncurkan kembali.


menggunakan HSM adalah cara yang ideal: en.wikipedia.org/wiki/Hardware_security_module
atom88

-8

Jika Anda menggunakan java 8 penggunaan encoder dan decoder Base64 internal dapat dihindari dengan mengganti

return new BASE64Encoder().encode(bytes);

dengan

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

dan

return new BASE64Decoder().decodeBuffer(property);

dengan

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

Perhatikan bahwa solusi ini tidak melindungi data Anda karena metode dekripsi disimpan di tempat yang sama. Itu hanya membuatnya lebih sulit untuk istirahat. Terutama itu menghindari untuk mencetaknya dan menunjukkannya kepada semua orang secara tidak sengaja.


26
Base64 bukan enkripsi.
jwilleke

1
Base64 bukan aplikasi dan itu adalah contoh terburuk yang dapat Anda berikan ... banyak orang percaya bahwa base64 adalah algortihm enkripsi, jadi lebih baik untuk tidak membingungkan mereka ...
robob

perhatikan bahwa metode decode () di bagian atas file sumber melakukan enkripsi yang sebenarnya. Namun, pengkodean dan penguraian base64 ini diperlukan untuk mengkonversi dari string byte untuk melewatkan fungsi ini sesuatu yang dapat digunakannya (byte array byte [])
atom88
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.