¿Cómo puedo proteger el nombre de usuario y la contraseña de MySQL para que no se descompile?

87

Los .classarchivos 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?

Jakob Cosoroaba
fuente
Espero que no le importe que haya vuelto a etiquetar su pregunta. Eliminé "nombre de usuario" y "contraseña" y agregué "ingeniería inversa" y "descompilación". Creo que son más descriptivos que los originales. ¡Excelente pregunta sobre los fundamentos, por cierto!
William Brendel
6
Tenga en cuenta que el hecho de que esté utilizando Java no es realmente relevante aquí. Tener contraseñas codificadas en cualquier idioma es problemático de la misma manera ("strings thebinary" también muestra constantes de cadena en los programas C).
Joachim Sauer
@saua: Cierto, pero tal vez alguien publique algún código de muestra sobre cómo desacoplar nombres de usuario y contraseñas en Java. Incluso podría hacerlo yo mismo si tuviera tiempo.
William Brendel
1
Me he dado cuenta de que muchas respuestas piensan que está tratando de ocultar el nombre de usuario / contraseñas de un usuario no autorizado mientras el usuario que ejecuta la aplicación está bien. Creo que quieres ocultar la contraseña a TODOS . Aclare esto en la pregunta.
pek
1
Por ejemplo, digamos que las credenciales se utilizan para conectarse a un servidor en el que nadie más que la aplicación puede iniciar sesión.
pek

Respuestas:

123

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 setCredentialsmé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 getUsernamey getPasswordpara 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.userNodeForPackagecreará 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 Preferenceclase 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:

  1. El cliente se autentica con el nivel de lógica empresarial utilizando el nombre de usuario / contraseña personal del usuario. El 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 de lógica empresarial 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 a procedimiento remoto como getInventoryList.
  3. El nivel de lógica empresarial se conecta a la base de datos y recupera la información solicitada. El nivel de lógica de negocios se encarga de formar una consulta SQL segura basada en la solicitud del usuario. Todos los parámetros de la consulta SQL deben desinfectarse para evitar ataques de inyección SQL.
  4. El nivel de lógica empresarial 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 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.

William Brendel
fuente
4
¿Cómo evita exactamente esto que alguien obtenga el nombre de usuario / contraseña? ¿No puedes simplemente leerlo del archivo?
Joe Phillips
Como dije en mi respuesta, si sus permisos de archivo están configurados correctamente, solo el usuario que ejecuta el programa tiene acceso de lectura a ese archivo de preferencias. En entornos UNIX, esto se hace automáticamente. Windows puede requerir pasos adicionales (realmente no estoy seguro, ya que no uso mucho Windows).
William Brendel
Creo que la idea es que el usuario que ejecuta la aplicación no es el que está tratando de evitar. Si ese es el caso, entonces tendrá que cifrarlo.
Michael Haren
Sí, Michael tiene razón. Básicamente, la idea es que ya conozca el nombre de usuario / contraseña, por lo que no es necesario que se lo oculte. Sin embargo, se ocultará a otros usuarios a través de permisos de archivo.
William Brendel
7
Si está implementando (ejemplo) una aplicación de edición de base de datos para un usuario y no desea que sepan el nombre de usuario y la contraseña de la base de datos, entonces ha diseñado la solución de manera incorrecta y el software cliente debería comunicarse con un servidor (a través de por ejemplo, un servicio web) que hace las cosas de DB.
JeeBee
15

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.

Keltia
fuente
7
Si bien eso hace que sea un poco más fácil cambiar las contraseñas más adelante, no resuelve el problema básico de seguridad.
Brian Knoblauch
Si lo hace. Véase también la respuesta de William Brendel.
Keltia
1
El método que Keltia y yo señalamos es la forma aceptada de abordar este problema. Desacoplar sus credenciales de inicio de sesión de su código compilado es una de las prácticas de seguridad de software más básicas. Poner las credenciales de inicio de sesión en un archivo separado es una forma eficaz de lograrlo.
William Brendel
Además, las restricciones del sistema operativo deben cancelar el hecho de que la información de configuración esté en un archivo de texto sin formato. Por ejemplo, en UNIX, el archivo de texto sin formato debe ser propiedad del usuario que ejecuta el programa y tener los permisos 0600, por lo que solo el propietario puede leerlo.
William Brendel
4
De acuerdo, el archivo solo puede ser leído por el usuario que ejecuta el programa. Excelente. Eso no resuelve nada. :-) Yo, el usuario del que intentamos mantener en secreto la contraseña, puedo leerla tan fácilmente como la aplicación ...
Brian Knoblauch
3

¿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.

Tim Howland
fuente
el enlace proporcionado es malo
James Oravec
2

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:

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

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).

Caner
fuente
1

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

Aaron Digulla
fuente
1
En algún lugar del código fuente todavía es necesario descifrar la contraseña cifrada para crear la conexión, esta contraseña considerada todavía está allí.
Bear0x3f
0

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.

renegado
fuente
He oído hablar de personas que generan todas las combinaciones posibles de cadenas y almacenan sus correspondientes hashes MD5. Entonces, cuando encuentran el hash MD5 de alguien, simplemente encuentran el hash que almacenaron y obtienen la cadena correspondiente.
Navegación