¿Este método de cifrado / almacenamiento de datos es seguro?

8

Primero, permítame decir que Sé que es una mala idea almacenar información confidencial en una base de datos mysql, así que no responda diciendo "NO LO HAGA" o algo así. efecto. Estoy construyendo un sitio web para el que es absolutamente esencial almacenar números de Seguro Social, y tengo que poder recuperar esos datos de la base de datos (sin hashing).

Dicho esto, he investigado la mejor manera de cifrar / descifrar los datos y he creado una función personalizada para manejar el cifrado. Aquí está mi función de cifrado:

function my_data_encrypt($value){
    $salt=substr(uniqid('', true), 0, 20);
    $key=$salt.MY_PRIVATE_KEY;
    $enc_value=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $value, MCRYPT_MODE_CBC, md5(md5($key))));
    return array("enc_value"=>$enc_value, "salt"=>$salt);
}

Básicamente, estoy generando una cadena aleatoria para mi sal, y luego agregando a la sal una clave privada MY_PRIVATE_KEY que se define en un archivo de configuración independiente. Luego uso mcrypt_encrypt para cifrar los datos, luego base64_encode para hacer que el cifrado sea seguro para almacenar en la base de datos. La cadena cifrada y la sal única se devuelven y almacenan en la base de datos juntos.

Mi pensamiento fue que agregar una "clave privada" que se almacena en el archivo de configuración (no en la base de datos) en la mezcla agregaría otro nivel de seguridad al cifrado, de esa manera incluso si alguien piratea la base de datos y obtiene el cifrado valor y la sal, todavía no tendrían todo lo que necesitan para descifrar los datos.

¿Pueden los expertos en seguridad revisar mi función y hacerme saber si / cómo mis datos pueden ser pirateados y si hay algo más que pueda hacer para mejorarlos?

    
pregunta BWDesign 10.05.2013 - 12:27
fuente

2 respuestas

17

Está utilizando una clave de 128 bits (salida de MD5, por lo tanto de 128 bits) con un cifrado de bloque que espera una clave de 256 bits (Rijndael-256). Si se implementara PHP correctamente, te estallaría en ese momento. Sin embargo, la documentación indica que la clave simplemente se rellena con ceros hasta la longitud requerida. Si usa una clave de 128 bits, es mejor que use un cifrado con una clave de 128 bits, es decir, Rijndael-128.

La "sal" es inútil o mal aplicada. Las sales están destinadas a tratar con claves débiles, que pueden recuperarse de forma realista mediante una búsqueda exhaustiva, es decir, cuando las claves son contraseñas . Si su clave es una clave real, correctamente generada, gorda y aleatoria, entonces la sal es inútil. Por otro lado, si su clave es una contraseña, entonces está en el real de hashing de contraseña , lo que requerirá más esfuerzo.

Por otro lado, el IV debe ser aleatorio, en lugar de ser el hash de la clave. El uso apropiado del cifrado de bloque sería:

  • Tenga una clave única de 128 bits, generada con un PRNG criptográficamente fuerte (por ejemplo, /dev/urandom on Sistemas Linux).

  • Al cifrar un mensaje nuevo, cree un nuevo IV aleatorio con un PRNG fuerte (es decir, openssl_random_pseudo_bytes () , que se alimenta internamente de /dev/urandom o equivalente).

  • Olvídate de cualquier "sal". Cifre con su clave principal y el IV generado aleatoriamente. Luego, debe almacenar el IV junto con el mensaje cifrado (para un SSN, obtendrá 32 bytes: 16 para el IV y 16 para el bloque cifrado).

Luego se aplican las siguientes advertencias:

  • Esto es cifrado; garantiza confidencialidad , no integridad . Alguien que pueda modificar partes de su base de datos puede causar estragos en su SSN (aunque solo sea cambiando entre usuarios). La integridad de la base de datos es un problema difícil y las soluciones más conocidas no son buenas; implican gastos generales significativos.

  • La confidencialidad se mantiene solo mientras no se revele la clave. Pero imaginamos un atacante que obtuviera un acceso de lectura excepcionalmente privilegiado a los contenidos del servidor, y el servidor, en general, conoce la clave. Si mantiene la clave en un archivo, separado de la base de datos, puede obtener alguna protección adicional contra los ataques de inyección de SQL básicos, que solo funcionan en el contenido de la base de datos; pero tenga en cuenta que solo ha solucionado algunas vulnerabilidades.

respondido por el Thomas Pornin 10.05.2013 - 13:26
fuente
8

Lamento haberte dicho esto: estás asumiendo riesgos muy peligrosos con esos números de seguro social. Definamos rápidamente algo según los estándares PCI-DSS:

  

P: ¿Qué se define como 'datos del titular de la tarjeta'?   R: Los datos del titular de la tarjeta son todos los datos de identificación personal asociados con un titular de la tarjeta. Esto podría ser un número de cuenta, fecha de caducidad, nombre, dirección, número de seguro social, etc. Toda la información de identificación personal asociada con el titular de la tarjeta que se almacena, procesa o transmite también se considera información del titular de la tarjeta.

PCI DSS es el estándar para el almacenamiento seguro de información (preferiblemente financiero). Las reglas son estrictas y las multas por no cumplir son masivas ($ 5k - $ 100k por mes). Sin embargo, los SSN, pueden almacenarse de acuerdo con esto, pero nuevamente, las pautas son estrictas ( página 14 de este documento ). Por lo tanto, incluso antes de discutir su implementación actual, le sugiero que sepa en qué se está metiendo.

Pongamos algunas cosas también:

  1. Si está retransmitiendo los SSN (en una página o de otra manera) y su capa de transporte no es segura, es como si no importaran todos los niveles de cifrado a los que ingresó los datos en el back-end. Olfatear es muy fácil en estos días.
  2. Si está diseñando su propio "paquete de cifrado" porque cree que funcionará, ¿recuerda a todas las empresas que tuvieron violaciones? Pensaron que sus cosas también funcionarían.

Vayamos a los detalles. Cualquiera que no esté familiarizado con PHP (otros proveedores de respuestas), la función mcrypt_encrypt toma como parámetros:

  • Cipher - here, 256bit AES
  • PK
  • texto sin formato
  • Modo (aquí, CBC)
  • IV

Vamos a romper tu función un poco. El primer punto obvio de "WTF es esta mierda" es tu uso de md5 para generar claves e IVs. Eres consciente de que MD5 tiene serias vulnerabilidades, ¿no?

El segundo WTF obvio es la elección del cifrado. AES 512 es utilizable en PHP. Esto también duplicaría el tamaño de tu llave. No solo esto, sino que el uso de la sal es bastante divertido, ¡para esto es un IV!

El tercer WTF obvio es el uso de una constante para su clave privada. Se da cuenta de que esta constante persistirá desde la inicialización hasta el cierre de su script, ¿verdad? No solo eso, pero apuesto a que está definido en texto claro en uno de los archivos. Esto significa que:

  • Si alguien se las arregla para encontrar una SINGLE incluida / lectura de archivo vulnera en su sitio, tienen su clave privada
  • Si alguien logra encontrar un código SINGLE vulnera su sitio, tiene su clave privada

Considere almacenar su clave privada de forma segura (o incluso mejor, solo guardar un hash de la misma y requerir la validación por parte del usuario de la clave para descifrar, lo que obliga al usuario a conocer la clave para descifrar información sensible).

TL; DR: lea sobre PCI-DSS, deje de inventar y entienda las herramientas que está usando. Para empezar, recomendaría una cartilla en criptografía: ¿alguna objeción a Bruce Schneier? Si no, seleccione Criptografía práctica (o si prefiere libros más blandos, Secretos y mentiras, suponiendo que haya una edición más reciente que la de 2001).

    
respondido por el Sébastien Renauld 10.05.2013 - 13:25
fuente

Lea otras preguntas en las etiquetas