Los .class
archivos Java se pueden descompilar con bastante facilidad. ¿Cómo puedo proteger mi base de datos si tengo que usar los datos de inicio de sesión en el código?
Los .class
archivos Java se pueden descompilar con bastante facilidad. ¿Cómo puedo proteger mi base de datos si tengo que usar los datos de inicio de sesión en el código?
Respuestas:
Nunca codifique contraseñas en su código. Esto se mencionó recientemente entre los 25 errores de programación más peligrosos :
La codificación rígida de una cuenta secreta y una contraseña en su software es extremadamente conveniente, para ingenieros en reversa calificados. Si la contraseña es la misma en todo su software, todos los clientes se vuelven vulnerables cuando esa contraseña inevitablemente se da a conocer. Y debido a que está codificado de forma rígida, es un gran problema arreglarlo.
Debe almacenar la información de configuración, incluidas las contraseñas, en un archivo separado que la aplicación lee cuando se inicia. Esa es la única forma real de evitar que la contraseña se filtre como resultado de la descompilación (nunca la compile en el binario para empezar).
Para obtener más información sobre este error común, puede leer el artículo CWE-259 . El artículo contiene una definición más completa, ejemplos y mucha más información sobre el problema.
En Java, una de las formas más sencillas de hacer esto es utilizar la clase Preferencias. Está diseñado para almacenar todo tipo de configuraciones de programas, algunas de las cuales podrían incluir un nombre de usuario y una contraseña.
import java.util.prefs.Preferences;
public class DemoApplication {
Preferences preferences =
Preferences.userNodeForPackage(DemoApplication.class);
public void setCredentials(String username, String password) {
preferences.put("db_username", username);
preferences.put("db_password", password);
}
public String getUsername() {
return preferences.get("db_username", null);
}
public String getPassword() {
return preferences.get("db_password", null);
}
// your code here
}
En el código anterior, puede llamar al setCredentials
método después de mostrar un cuadro de diálogo solicitando el nombre de usuario y la contraseña. Cuando necesite conectarse a la base de datos, puede usar los métodos getUsername
y getPassword
para recuperar los valores almacenados. Las credenciales de inicio de sesión no estarán codificadas en sus archivos binarios, por lo que la descompilación no representará un riesgo de seguridad.
Nota importante: los archivos de preferencias son archivos XML de texto sin formato. Asegúrese de tomar las medidas adecuadas para evitar que usuarios no autorizados vean los archivos sin formato (permisos de UNIX, permisos de Windows, etc.). En Linux, al menos, esto no es un problema, porque la llamada Preferences.userNodeForPackage
creará el archivo XML en el directorio de inicio del usuario actual, que de todos modos no es legible por otros usuarios. En Windows, la situación puede ser diferente.
Notas más importantes: ha habido mucha discusión en los comentarios de esta respuesta y otras sobre cuál es la arquitectura correcta para esta situación. La pregunta original realmente no menciona el contexto en el que se está utilizando la aplicación, así que hablaré sobre las dos situaciones que se me ocurren. El primero es el caso en el que la persona que usa el programa ya conoce (y está autorizada a conocer) las credenciales de la base de datos. El segundo es el caso en el que usted, el desarrollador, está tratando de mantener en secreto las credenciales de la base de datos de la persona que usa el programa.
Primer caso: el usuario está autorizado a conocer las credenciales de inicio de sesión de la base de datos
En este caso, la solución que mencioné anteriormente funcionará. La Preference
clase Java almacenará el nombre de usuario y la contraseña en texto sin formato, pero el archivo de preferencias solo podrá leerlo el usuario autorizado. El usuario puede simplemente abrir el archivo XML de preferencias y leer las credenciales de inicio de sesión, pero eso no es un riesgo de seguridad porque el usuario conocía las credenciales para empezar.
Segundo caso: intentar ocultar las credenciales de inicio de sesión del usuario
Este es el caso más complicado: el usuario no debe conocer las credenciales de inicio de sesión, pero aún necesita acceso a la base de datos. En este caso, el usuario que ejecuta la aplicación tiene acceso directo a la base de datos, lo que significa que el programa necesita conocer las credenciales de inicio de sesión con anticipación. La solución que mencioné anteriormente no es apropiada para este caso. Puede almacenar las credenciales de inicio de sesión de la base de datos en un archivo de preferencias, pero el usuario podrá leer ese archivo, ya que será el propietario. De hecho, no existe una buena forma de utilizar este estuche de forma segura.
Caso correcto: uso de una arquitectura de varios niveles
La forma correcta de hacerlo es tener una capa intermedia, entre su servidor de base de datos y su aplicación cliente, que autentique a los usuarios individuales y permita realizar un conjunto limitado de operaciones. Cada usuario tendría sus propias credenciales de inicio de sesión, pero no para el servidor de la base de datos. Las credenciales permitirían el acceso a la capa intermedia (el nivel de lógica empresarial) y serían diferentes para cada usuario.
Cada usuario tendría su propio nombre de usuario y contraseña, que podrían almacenarse localmente en un archivo de preferencias sin ningún riesgo de seguridad. Esto se denomina arquitectura de tres niveles (los niveles son el servidor de base de datos, el servidor de lógica empresarial y la aplicación cliente). Es más complejo, pero realmente es la forma más segura de hacer este tipo de cosas.
El orden básico de operaciones es:
getInventoryList
.Tenga en cuenta que en todo el proceso, la aplicación cliente nunca se conecta directamente a la base de datos . El nivel de lógica empresarial recibe una solicitud de un usuario autenticado, procesa la solicitud del cliente de una lista de inventario y solo entonces ejecuta una consulta SQL.
Coloque la contraseña en un archivo que leerá la aplicación. NUNCA incruste contraseñas en un archivo fuente. Período.
Ruby tiene un módulo poco conocido llamado DBI :: DBRC para tal uso. No tengo ninguna duda de que Java tiene un equivalente. De todos modos, no es difícil escribir uno.
¿Estás escribiendo una aplicación web? Si es así, utilice JNDI para configurarlo externamente a la aplicación. Una descripción general está disponible aquí :
JNDI proporciona una forma uniforme para que una aplicación busque y acceda a servicios remotos a través de la red. El servicio remoto puede ser cualquier servicio empresarial, incluido un servicio de mensajería o un servicio específico de la aplicación, pero, por supuesto, una aplicación JDBC está interesada principalmente en un servicio de base de datos. Una vez que se crea un objeto DataSource y se registra con un servicio de nombres JNDI, una aplicación puede usar la API JNDI para acceder a ese objeto DataSource, que luego se puede usar para conectarse a la fuente de datos que representa.
No importa lo que haga, la información confidencial se almacenará en algún archivo en algún lugar. Tu objetivo es hacer que sea lo más difícil de conseguir. Cuánto de esto puede lograr depende de su proyecto, las necesidades y el grosor de la billetera de su empresa.
La mejor manera es no almacenar contraseñas en ningún lugar. Esto se logra mediante el uso de funciones hash para generar y almacenar hash de contraseña:
hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366
Los algoritmos hash son funciones unidireccionales. Convierten cualquier cantidad de datos en una "huella digital" de longitud fija que no se puede revertir. También tienen la propiedad de que si la entrada cambia aunque sea un poquito, el hash resultante es completamente diferente (vea el ejemplo anterior). Esto es excelente para proteger las contraseñas, porque queremos almacenar las contraseñas en una forma que las proteja incluso si el archivo de contraseñas está comprometido, pero al mismo tiempo, necesitamos poder verificar que la contraseña de un usuario sea correcta.
Nota no relacionada: en los viejos tiempos de Internet, cuando hacías clic en el enlace "olvidé mi contraseña", los sitios web te enviarían por correo electrónico tu contraseña de texto sin formato. Probablemente los estaban almacenando en una base de datos en algún lugar. Cuando los piratas informáticos obtuvieran acceso a su base de datos, obtendrían acceso a todas las contraseñas. Dado que muchos usuarios usarían la misma contraseña en varios sitios web, este fue un gran problema de seguridad. Afortunadamente, hoy en día esta no es la práctica común.
Ahora viene la pregunta: ¿cuál es la mejor manera de almacenar contraseñas? Consideraría esta solución ( stormpath del servicio de autenticación y administración de usuarios) bastante ideal:
Obviamente, no eres Google ni un banco, por lo que esta es una solución exagerada para ti. Pero luego surge la pregunta: ¿Cuánta seguridad requiere tu proyecto, cuánto tiempo y dinero tienes?
Para muchas aplicaciones, aunque no se recomienda, almacenar una contraseña codificada en el código puede ser una solución suficientemente buena. Sin embargo, al agregar fácilmente un par de pasos adicionales de seguridad de la lista anterior, puede hacer que su aplicación sea mucho más segura.
Por ejemplo, supongamos que el paso 1 no es una solución aceptable para su proyecto. No desea que los usuarios ingresen la contraseña cada vez, o ni siquiera desea / necesita que los usuarios conozcan la contraseña. Aún tienes información confidencial en algún lugar y quieres protegerla. Tiene una aplicación simple, no hay un servidor para almacenar sus archivos o esto es demasiado complicado para su proyecto. Su aplicación se ejecuta en entornos donde no es posible tener archivos almacenados de forma segura. Este es uno de los peores casos, pero aún con alguna medida de seguridad adicional, puede tener una solución mucho más segura. Por ejemplo, puede almacenar la información confidencial en un archivo y puede cifrar el archivo. Puede tener la clave privada de cifrado codificada en el código. Puede ofuscar el código, de modo que le resulte un poco más difícil a alguien descifrarlo.este enlace . (Quiero advertirle una vez más que esto no es 100% seguro. Un hacker inteligente con el conocimiento y las herramientas adecuadas puede piratear esto. Pero según sus requisitos y necesidades, esta podría ser una solución suficientemente buena para usted).
Esta pregunta muestra cómo almacenar contraseñas y otros datos en un archivo cifrado: Cifrado basado en contraseñas AES de 256 bits de Java
MD5 es un algoritmo hash, no un algoritmo de cifrado, en resumen, no puede recuperar el hash, solo puede comparar. Idealmente, debería usarse al almacenar la información de autenticación del usuario y no el nombre de usuario y la contraseña de la base de datos. db username y pwd deben estar encriptados y guardados en un archivo de configuración, para hacer lo mínimo.