Asegurar una contraseña en un archivo de propiedades

Tengo una aplicación Java que se conecta a una base de datos.
El nombre de usuario y la contraseña de la base de datos se almacenan en un archivo de propiedades.
¿Cuál es la práctica común para evitar almacenar la contraseña en texto sin formato en el archivo de propiedades sin perder la opción de permitir que el usuario la modifique?
La principal motivación aquí es evitar que alguien mire por encima del hombro del administrador y vea la contraseña mientras el administrador está editando el archivo de propiedades.
Leí aquí que hay una forma integrada de hacerlo en C #.
Conociendo Java, no espero encontrar una solución integrada, pero me gustaría escuchar lo que otras personas están haciendo.
Si no encuentro una buena opción, probablemente cifraré con una contraseña constante que se mantendrá en el código. Pero odiaría hacerlo de esta manera porque se siente mal.

Editar 12 de diciembre 2012 Parece que no hay magia y debo guardar la contraseña en el código o algo similar. Al final implementamos algo muy similar a lo que Jasypt que se mencionó en una de las respuestas sí lo hace. Así que estoy aceptando la respuesta de Jasypt porque es lo más cercano a una respuesta definitiva.

enter image description here

Jasypt proporciona la clase org.jasypt.properties.EncryptableProperties para cargar, administrar y descifrar de forma transparente los valores cifrados en archivos .properties, lo que permite la combinación de valores cifrados y no cifrados en el mismo archivo.

http://www.jasypt.org/encrypting-configuration.html

Al usar un objeto org.jasypt.properties.EncryptableProperties, una aplicación podría leer y usar correctamente un archivo .properties como este:

datasource.driver=com.mysql.jdbc.Driver datasource.url=jdbc:mysql://localhost/reportsdb datasource.username=reportsUser datasource.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm) 

Tenga en cuenta que la contraseña de la base de datos está encriptada (de hecho, cualquier otra propiedad también podría estar encriptada, ya sea relacionada con la configuración de la base de datos o no).

¿Cómo leemos este valor? Me gusta esto:

 /* * First, create (or ask some other component for) the adequate encryptor for * decrypting the values in our .properties file. */ StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setPassword("jasypt"); // could be got from web, env variable... /* * Create our EncryptableProperties object and load it the usual way. */ Properties props = new EncryptableProperties(encryptor); props.load(new FileInputStream("/path/to/my/configuration.properties")); /* * To get a non-encrypted value, we just get it with getProperty... */ String datasourceUsername = props.getProperty("datasource.username"); /* * ...and to get an encrypted value, we do exactly the same. Decryption will * be transparently performed behind the scenes. */ String datasourcePassword = props.getProperty("datasource.password"); // From now on, datasourcePassword equals "reports_passwd"... 

La solución de compromiso de los pobres es usar un enfoque simplista de múltiples firmas.

Por ejemplo, el DBA establece la contraseña de la base de datos de aplicaciones en una cadena aleatoria de 50 caracteres. TAKqWskc4ncvKaJTyDcgAHq82X7tX6GfK2fc386bmNw3muknjU

Él o ella le dan la mitad de la contraseña al desarrollador de la aplicación que luego la codifica en el binario de Java.

private String pass1 = “TAKqWskc4ncvKaJTyDcgAHq82”

La otra mitad de la contraseña se pasa como un argumento de línea de comando. el DBA otorga el pase 2 al soporte del sistema o la persona de administración que ingresa la hora de inicio de la aplicación o la coloca en el script de inicio automático de la aplicación.

java -jar /myapplication.jar -pass2 X7tX6GfK2fc386bmNw3muknjU

Cuando se inicia la aplicación, usa pass1 + pass2 y se conecta a la base de datos.

Esta solución tiene muchas ventajas sin las caídas mencionadas.

Puede poner la mitad de la contraseña de manera segura en los argumentos de la línea de comando, ya que leerlo no le ayudará mucho a menos que sea el desarrollador que tiene la otra mitad de la contraseña.

El DBA también puede cambiar la segunda mitad de la contraseña y el desarrollador no necesita tener que volver a implementar la aplicación.

El código fuente también puede ser semi público como leerlo y la contraseña no le dará acceso a la aplicación.

Puede mejorar aún más la situación agregando restricciones en los rangos de direcciones IP de las que la base de datos aceptará conexiones.

¿Qué hay de proporcionar un mecanismo de autenticación de N-Factor personalizado?

Antes de combinar los métodos disponibles, supongamos que podemos realizar lo siguiente:

1) Código duro dentro del progtwig Java

2) Almacenar en un archivo .properties

3) Pedir al usuario que escriba la contraseña desde la línea de comandos

4) Pedir al usuario que escriba la contraseña de un formulario

5) Pedir al usuario que cargue un archivo de contraseña desde la línea de comandos o un formulario

6) Proporcione la contraseña a través de la red

7) muchas alternativas (por ejemplo, Draw A Secret, Fingerprint, IP-specific, bla bla bla)

Primera opción: podemos hacer las cosas más complicadas para un atacante mediante el uso de ofuscación, pero esto no se considera una buena contramedida. Un buen codificador puede entender fácilmente cómo funciona si él / ella puede acceder al archivo. Incluso podríamos exportar un binario por usuario (o simplemente la parte de ofuscación o pieza clave), por lo que un atacante debe tener acceso a este archivo específico del usuario, no a otra distribución. Una vez más, deberíamos encontrar una manera de cambiar las contraseñas, por ejemplo, recomstackndo o usando la reflexión para el comportamiento de la clase de cambio sobre la marcha.

Segunda opción: podemos almacenar la contraseña en el archivo .properties en un formato cifrado, por lo que no es directamente visible desde un atacante (al igual que jasypt ). Si necesitamos un administrador de contraseñas también necesitaremos una contraseña maestra que nuevamente debería almacenarse en algún lugar: dentro de un archivo .class, el almacén de claves, kernel, otro archivo o incluso en la memoria, todos tienen sus pros y sus contras.
Pero ahora los usuarios solo editarán el archivo .properties para cambiar la contraseña.

3ª opción: escriba la contraseña cuando se ejecuta desde la línea de comandos, p. java -jar /myprogram.jar -p sdflhjkiweHIUHIU8976hyd . java -jar /myprogram.jar -p sdflhjkiweHIUHIU8976hyd .

Esto no requiere que la contraseña sea almacenada y permanecerá en la memoria. Sin embargo, los comandos de history y los registros del sistema operativo, pueden ser sus peores enemigos aquí. Para cambiar contraseñas sobre la marcha, deberá implementar algunos métodos (por ejemplo, escuchar las entradas de la consola, RMI, tomas de stream, REST bla bla bla), pero la contraseña siempre permanecerá en la memoria.

Incluso se puede descifrar temporalmente solo cuando sea necesario -> luego eliminar el descifrado, pero siempre mantener la contraseña encriptada en la memoria. Desafortunadamente, el método mencionado anteriormente no aumenta la seguridad contra el acceso en memoria no autorizado, porque la persona que lo logra, probablemente tendrá acceso al algoritmo, la sal y cualquier otro secreto que se utilice.

Cuarta opción: proporcione la contraseña de un formulario personalizado, en lugar de la línea de comando. Esto evitará el problema de la exposición a la explotación forestal.

5ta opción: proporcione un archivo como una contraseña almacenada anteriormente en otro medio -> luego elimine el archivo con dificultad. Esto evitará nuevamente el problema de la exposición a la explotación maderera, además de que no se requiere tipeo alguno que pueda ser robado al hombro. Cuando se requiera un cambio, proporcione otro archivo, luego elimínelo nuevamente.

Opción 6: una vez más para evitar la navegación por el hombro, se puede implementar una llamada al método RMI para proporcionar la contraseña (a través de un canal encriptado) desde otro dispositivo, por ejemplo, a través de un teléfono móvil. Sin embargo, ahora necesita proteger su canal de red y acceder al otro dispositivo.

Elegiría una combinación de los métodos anteriores para lograr la máxima seguridad, así que uno tendría que acceder a los archivos .class, el archivo de propiedades, los registros, el canal de red, el hombro de navegación, el hombre en el medio, otros archivos bla bla bla. Esto se puede implementar fácilmente usando una operación XOR entre todas las sub_contraseña para producir la contraseña real.

Sin embargo, no podemos protegernos del acceso en memoria no autorizado, esto solo se puede lograr utilizando hardware de acceso limitado (por ejemplo, tarjetas inteligentes, HSM, SGX), donde todo se computa en ellos, sin que nadie, incluso el propietario legítimo sea capaz de acceder a claves de descifrado o algoritmos. De nuevo, también se puede robar este hardware, se han reportado ataques de canal lateral que pueden ayudar a los atacantes en la extracción de claves y en algunos casos es necesario confiar en otra parte (por ejemplo, con SGX confías en Intel). Por supuesto, la situación puede empeorar cuando sea posible la clonación segura en el enclave (desmantelamiento), pero supongo que llevará algunos años ser práctica.

Además, uno puede considerar una solución de intercambio de claves donde la clave completa se divide entre diferentes servidores. Sin embargo, después de la reconstrucción, la llave completa puede ser robada. La única forma de mitigar el problema mencionado anteriormente es mediante un cálculo multiparte seguro .

Siempre debemos tener en cuenta que sea cual sea el método de entrada, debemos asegurarnos de que no somos vulnerables a la detección de redes (ataques MITM) y / o registradores de teclas.

En realidad, este es un duplicado de la contraseña de cifrado en los archivos de configuración? .

La mejor solución que encontré hasta ahora está en este Answert: https://stackoverflow.com/a/1133815/1549977

Pros: la contraseña se guarda como una matriz de caracteres, no como una cadena. Todavía no es bueno, pero es mejor que cualquier otra cosa.