modificar leer desempaquetar descompilar descompilador descargar decompilador con archivo java mysql security reverse-engineering decompiling

desempaquetar - leer.class java



¿Cómo puedo proteger el nombre de usuario y la contraseña de MySQL de la descompilación? (6)

Los archivos .class 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?


¿Estás escribiendo una aplicación web? Si es así, use JNDI para configurarlo externamente a la aplicación. Una descripción general está disponible here :

JNDI proporciona una forma uniforme para que una aplicación encuentre 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 un objeto DataSource se crea 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 utilizar para conectarse a la fuente de datos que representa.


Coloque la contraseña en un archivo que la aplicación leerá. NUNCA incruste contraseñas en un archivo fuente. Período.

Ruby tiene un módulo poco conocido llamado DBI::DBRC para dicho uso. No tengo dudas de que Java tiene un equivalente. De todos modos, no es difícil escribir uno.



MD5 es un algoritmo hash, no un algoritmo de encriptación, en resumen, no puedes volver a obtener hashed, solo puedes compararlo. Idealmente, debe utilizarse cuando se almacena la información de autenticación del usuario y no el nombre de usuario y la contraseña. El nombre de usuario db y pwd se deben cifrar y mantener en un archivo de configuración, para hacer lo mínimo.


No importa lo que hagas, la información confidencial se almacenará en algún archivo en alguna parte. Tu objetivo es hacer que sea tan difícil de obtener como sea posible. La cantidad de esto que puede lograr depende de su proyecto, necesidades y grosor de la billetera de su empresa.

La mejor manera es no almacenar contraseñas en ningún lado. Esto se logra mediante el uso de funciones hash para generar y almacenar hash de contraseñas:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

Los algoritmos Hash son funciones de una sola vía. Convierten cualquier cantidad de datos en una "huella dactilar" de longitud fija que no se puede revertir. También tienen la propiedad de que si la entrada cambia aunque sea un poco, el hash resultante es completamente diferente (ver el ejemplo anterior). Esto es ideal para proteger las contraseñas, porque queremos almacenar contraseñas de una forma que las proteja incluso si el archivo de contraseñas está comprometido, pero al mismo tiempo, debemos poder verificar que la contraseña de un usuario sea correcta.

Nota no relacionada: en los viejos tiempos de Internet, cuando hacía clic en el enlace "Olvidé mi contraseña", los sitios web le enviaban por correo electrónico su contraseña de texto sin formato. Probablemente estaban almacenando esos en una base de datos en alguna parte. Cuando los hackers obtengan acceso a su base de datos, obtendrán acceso a todas las contraseñas. Dado que muchos usuarios utilizarían la misma contraseña en varios sitios web, este fue un gran problema de seguridad. Afortunadamente, hoy en día esta no es una práctica común.

Ahora viene la pregunta: ¿cuál es la mejor manera de almacenar contraseñas? Considero que esta solución (autenticación y servicio de gestión de usuarios stormpath) es una solución bastante ideal:

  1. Su usuario ingresa las credenciales, y esto se valida con el hash de la contraseña
  2. Los hashes de contraseña se generan y almacenan, no las contraseñas
  3. Hashes se realizan múltiples veces
  4. Hashes se generan utilizando una sal generada al azar
  5. Hashes se cifran con una clave privada
  6. La clave privada se almacena en un lugar físicamente diferente que hashes
  7. Las claves privadas se actualizan según la moda
  8. Los hashes cifrados se dividen en fragmentos
  9. Estos trozos se almacenan en ubicaciones físicamente separadas

Obviamente no eres el google o un banco, por lo que esta es una solución excesiva para ti. Pero luego viene la pregunta: ¿Cuánta seguridad requiere su proyecto, cuánto tiempo y dinero tiene?

Para muchas aplicaciones, aunque no se recomienda, almacenar una contraseña codificada en el código podría ser una solución lo suficientemente buena. Sin embargo, al agregar fácilmente algunos 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 incluso no quiere / necesita que los usuarios conozcan la contraseña. Aún tienes información confidencial en algún lado y quieres proteger esto. 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 encriptar el archivo. Puede tener la clave privada de cifrado codificada en el código. Puede ocultar el código, por lo que es un poco más difícil para alguien descifrarlo. Existen muchas bibliotecas para este propósito, consulte este enlace . (Quiero advertirle una vez más que esto no es 100% seguro. Un pirata informático inteligente con el conocimiento y las herramientas correctas pueden piratear esto. Pero según sus requisitos y necesidades, esta podría ser una solución lo suficientemente buena para usted).


Nunca codifique las contraseñas en su código. Esto se mencionó recientemente en el Top 25 de los 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 especializados en ingeniería inversa. Si la contraseña es la misma en todo su software, entonces cada cliente se vuelve vulnerable cuando esa contraseña inevitablemente se conoce. Y debido a que está codificado, es un gran dolor solucionarlo.

Debe almacenar 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 manera real de evitar que la contraseña se filtre como resultado de la descompilación (para empezar, nunca compilarla en el binario).

Para obtener más información acerca de este error común, puede leer el artículo CWE-259 . El artículo contiene una definición más completa, ejemplos y mucha otra información sobre el problema.

En Java, una de las formas más sencillas de hacerlo es usar la clase de Preferencias. Está diseñado para almacenar todo tipo de configuraciones de programa, algunas de las cuales podrían incluir un nombre de usuario y 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 método setCredentials después de mostrar un diálogo pidiendo 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 binarios, por lo que la descompilación no representará un riesgo de seguridad.

Nota importante: los archivos de preferencias son solo 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 llamar a Preferences.userNodeForPackage creará el archivo XML en el directorio de inicio del usuario actual, que de todos modos no será leído por otros usuarios. En Windows, la situación puede ser diferente.

Notas más importantes: Hubo mucha discusión en los comentarios de esta respuesta y en otros acerca de cuál es la arquitectura correcta para esta situación. La pregunta original no menciona realmente el contexto en el que se está utilizando la aplicación, por lo que hablaré de las dos situaciones en las que se me ocurre. El primero es el caso en el que la persona que usa el programa ya conoce (y está autorizada para saber) las credenciales de la base de datos. El segundo es el caso en el que usted, el desarrollador, intenta 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 clase Java Preference almacenará el nombre de usuario y la contraseña en texto plano, pero el archivo de preferencias solo será legible por el usuario autorizado. El usuario puede simplemente abrir el archivo XML de preferencias y leer las credenciales de inicio de sesión, pero ese no es un riesgo de seguridad porque el usuario ya sabía las credenciales.

Segundo caso: tratando de ocultar las credenciales de inicio de sesión del usuario

Este es el caso más complicado: el usuario no debe saber 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 saber las credenciales de inicio de sesión con anticipación. La solución que mencioné arriba 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, realmente no hay una buena manera de usar este caso 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 usuarios individuales y permita que se realice 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 de negocios) y serían diferentes para cada usuario.

Todos los usuarios tendrían 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 llama arquitectura de tres niveles (los niveles son su servidor de base de datos, servidor de lógica de negocios y aplicación de cliente). Es más complejo, pero realmente es la forma más segura de hacer este tipo de cosas.

El orden básico de las operaciones es:

  1. El cliente se autentica con el nivel lógico de negocios utilizando el nombre de usuario / contraseña personal del usuario. El nombre de usuario y la contraseña son conocidos por el usuario y no están relacionados con las credenciales de inicio de sesión de la base de datos de ninguna manera.
  2. Si la autenticación tiene éxito, el cliente realiza una solicitud al nivel lógico de negocios solicitando información de la base de datos. Por ejemplo, un inventario de productos. Tenga en cuenta que la solicitud del cliente no es una consulta SQL; es una llamada de procedimiento remoto como getInventoryList .
  3. El nivel de lógica de negocios se conecta a la base de datos y recupera la información solicitada. El nivel lógico de negocios se encarga de formar una consulta SQL segura en función de la solicitud del usuario. Todos los parámetros de la consulta SQL se deben desinfectar para evitar ataques de inyección SQL.
  4. El nivel lógico de negocios envía la lista de inventario a la aplicación cliente.
  5. El cliente muestra la lista de inventario al usuario.

Tenga en cuenta que en todo el proceso, la aplicación cliente nunca se conecta directamente a la base de datos . El nivel lógico empresarial recibe una solicitud de un usuario autenticado, procesa la solicitud del cliente para una lista de inventario y solo luego ejecuta una consulta SQL.