Dos tomas en el cifrado de dos vías de PHP, ¿cuál es preferible?

7

Necesito cifrar algunos datos y descifrarlos en un momento posterior. Los datos están vinculados a usuarios específicos. He reunido dos posibles soluciones ...

1 : el primero se deriva de los documentos oficiales (ejemplo # 1 @ enlace ):

function encrypt($toEncrypt)
{
    global $key;
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    return base64_encode($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $toEncrypt, MCRYPT_MODE_CBC, $iv));
}

function decrypt($toDecrypt)
{
    global $key;
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $toDecrypt = base64_decode($toDecrypt);
    return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
}

La clave se genera una vez usando:

pack('H*', bin2hex(openssl_random_pseudo_bytes(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC))));

1.1 : he notado que el resultado cifrado siempre termina en dos signos iguales ('=='). ¿Por qué? - El uso de bin2hex () y hex2bin () en encrypt () y decrypt () en lugar de base64_encode () / base64_decode () respectivamente no produce estos resultados.

1.2 : ¿El uso de bin2hex () / hex2bin () tendrá alguna consecuencia en el resultado (aparte de la longitud)?

1.3 : parece haber cierta discusión sobre si llamar o no una función de ajuste en el resultado de la devolución al descifrar (esto también se aplica a la solución a continuación). ¿Por qué sería necesario?

2 : la segunda solución viene de aquí, Stackoverflow ( enlace ):

function encrypt($key, $toEncrypt)
{
    return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $toEncrypt, MCRYPT_MODE_CBC, md5(md5($key))));
}

function decrypt($key, $toDecrypt)
{
    return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($toDecrypt), MCRYPT_MODE_CBC, md5(md5($key))), "
function encrypt($toEncrypt)
{
    global $key;
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    return base64_encode($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $toEncrypt, MCRYPT_MODE_CBC, $iv));
}

function decrypt($toDecrypt)
{
    global $key;
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $toDecrypt = base64_decode($toDecrypt);
    return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
}
"); }

Soy consciente de que ambos enfoques para el manejo de claves son intercambiables, a propósito los hice diferentes a fin de destacar posibles soluciones, siéntase libre de mezclar y combinar.

Personalmente, creo que el primero ofrece una seguridad más estricta, ya que tanto la clave como el vector de inicialización están correctamente aleatorizados. Sin embargo, la segunda solución ofrece alguna forma de no-previsibilidad ya que la clave es única para cada parte de los datos cifrados (a pesar de que sufre bajo la débil aleatorización de md5 ()). La clave podría ser, por ejemplo, el nombre del usuario.

3 : Entonces, ¿cuál es preferible? Estoy un poco en la oscuridad ya que la respuesta de Stackoverflow obtuvo la friolera de 105 votos. Otros pensamientos, consejos?

4 : ¡Pregunta de bonificación !: No soy increíblemente inteligente en cuanto a los aspectos de seguridad del servidor, pero, obviamente, el acceso a los archivos PHP expondría la clave, lo cual, como resultado directo, representaría el cifrado. inútil, asumiendo que el atacante también tiene acceso a la base de datos. ¿Hay alguna manera de ocultar la clave?

¡Gracias por leer y que tengas un buen día!

    
pregunta user2026991 09.10.2014 - 10:37
fuente

2 respuestas

1

Los dos cifrados son equivalentes. Puede agregar una envoltura para usar un enfoque como un reemplazo directo del otro:

/**
* Uses the one-parameter method as if it was the 2-parameter one.
*/
function buhlencrypt($keya, $data) {
   global $key;
   $key = $keya;
   return encrypt($data);
}

/**
* Uses the two-parameter method as if it was the one-parameter.
*/
function weidencrypt($data) {
   global $key;
   return encrypt($key, $data);
}

La "previsibilidad" de MD5 agrega un nivel insignificante si "inseguridad": el 99,999999% de los usuarios serán pirateados debido a que eligieron una contraseña trivial o la anotaron en su libreta de direcciones o la dejaron en una nota adhesiva amarilla el teclado. Un ataque profesional pirateará el servidor y recopilará todas las contraseñas en lugar de forzar una MD5 (ver más abajo, en la respuesta a la pregunta 4).

  

1.1: He notado que el resultado encriptado siempre termina en dos signos iguales ('=='). ¿Por qué? - El uso de bin2hex () y hex2bin () en encrypt () y decrypt () en lugar de base64_encode () / base64_decode () respectivamente no produce estos resultados.

La codificación

Base64 funciona al reemplazar grupos de tres bytes binarios (3 x 8 = 24 bits) con cuatro caracteres ASCII-6 (4 x 6 = 24 bits). Esto significa que el texto de entrada debe tener una longitud múltiplo de tres y la salida siempre tendrá una longitud múltiplo de cuatro.

Para garantizar esto, Base64 agrega un relleno especial a su salida para indicar si hay uno o dos caracteres "ignorables". Y dado que Rijndael produce una salida que es múltiplo de 16, el último bloque de 16 siempre se rellena hasta la longitud 18.

  

1.2: ¿El uso de bin2hex () / hex2bin () tendrá alguna consecuencia en el resultado (aparte de la longitud)?

Ninguna en absoluto.

  

1.3: Parece haber cierta discusión sobre si llamar o no a una función de ajuste en el resultado de devolución al descifrar

Esto se debe a que Rijndael también usa un bloque de tamaño fijo, por lo que si tuviera que cifrar 17 bytes, se dividiría en dos bloques de 16 bytes, dando 32 bytes de los cuales los últimos 15 son de relleno. Dado que Rijndael no hace ninguna disposición para indicar la eliminación de la almohadilla (por ejemplo, no almacena la longitud del texto claro), la salida tendrá un relleno a cero que puede requerir eliminación. En algunos contextos, esto puede no ser necesario, ya que se puede ignorar un relleno cero sin importar qué.

  

4: ¡Pregunta de bonificación !: No soy increíblemente inteligente con respecto a los aspectos de seguridad del servidor, pero obviamente obtener acceso a los archivos PHP expondría la clave, lo cual, como resultado directo, inutilizaría el cifrado, asumiendo que el atacante también tiene Acceso a la base de datos. ¿Hay alguna manera de ocultar la clave?

Si lo haces simétricamente, entonces el acceso a los archivos PHP también permitirá eliminar el oscurecimiento. Verifique qué nivel de acceso podría tener una vez en posesión de los archivos PHP. Si su acceso está cerca de completarse, tiene poco sentido ofuscar la clave.

Sin embargo, puede hacer esto, aunque de forma indirecta.

Agregue a su modelo de usuario tres propiedades (es decir, dos tablas de columnas) llamadas UserKey, RootKey y RootKeyOK. Cada usuario recibe un triplete.

Cuando un usuario se registra, usted genera un IV aleatorio y lo cifra con la contraseña del usuario, almacenándolo en UserKey. También lo almacena sin cifrar en RootKey y configura RootKeyOK en FALSE.

Cuando el usuario cambia la contraseña, utiliza la contraseña antigua para descifrar la Clave de usuario y volver a cifrarla con la nueva contraseña. No necesitas tocar las RootKeys en absoluto.

Cada vez que el administrador inicia sesión, busca en la base de datos de usuarios las claves que no están bien; para cada uno de ellos cifra la RootKey con la contraseña del administrador y establece RootKeyOK en TRUE. Esto se puede hacer automáticamente.

Ahora en tu base de datos tienes:

  • HashedPassword --- inutilizable para un atacante ya que es un hash.
  • UserKey --- inutilizable ya que está cifrado
  • RootKey --- utilizable, SI todavía está claro, pero solo para los datos de este usuario

Cuando el usuario ha iniciado sesión y usted necesita cifrar algo que solo él (y el administrador) pueden leer, use su contraseña (se conectó, así que está en la memoria) para descifrar la Clave de usuario, y use la Clave de usuario para cifrar la datos.

Por supuesto, si necesita cifrar algo cuando ni el usuario ni el administrador han iniciado sesión, este enfoque no funcionará : tendrá que almacenarlo sin cifrar con un indicador que indica que se debe cifrar, y hágalo tan pronto como el usuario o el administrador inicien sesión. Si bien puede hacer esto de forma transparente para ambos, en verdad los datos pueden permanecer en claro durante largos periodos en el servidor, vulnerable a la captura de la base de datos (desde robo remoto hasta robo de disco duro).

    
respondido por el LSerni 09.10.2014 - 15:31
fuente
0

Me gustó este openssl_encrypt , hay diferentes, puedes probar:

enlace

    
respondido por el T.Todua 18.01.2018 - 18:46
fuente

Lea otras preguntas en las etiquetas