Limpieza de contraseñas de usuario

98

¿Cómo debo eliminar o limpiar las contraseñas proporcionadas por el usuario antes de codificarlas y almacenarlas en mi base de datos?

Cuando los desarrolladores de PHP consideran usar hash en las contraseñas de los usuarios por motivos de seguridad, a menudo tienden a pensar en esas contraseñas como lo harían con cualquier otro dato proporcionado por el usuario. Este tema surge a menudo en preguntas de PHP relacionadas con el almacenamiento de contraseñas; el desarrollador a menudo quiere limpiar la contraseña usando funciones como escape_string()(en varias iteraciones) htmlspecialchars(), addslashes()y otras antes de aplicar el hash y almacenarla en la base de datos.

Jay Blanchard
fuente
1
Puede codificar el usuario en base64
MSS
No @MSS, no debería hacerlo porque base64 está codificando , no encriptando o hash . Las contraseñas siempre deben tener hash .
Jay Blanchard
1
Quiero decir antes de hash;)
MSS
No debe ni necesita hacer eso antes de aplicar el hash. Hará que tengas que escribir código adicional innecesario @MSS
Jay Blanchard

Respuestas:

99

Nunca debe escapar, recortar o usar ningún otro mecanismo de limpieza en las contraseñas que usará con PHP password_hash()por varias razones, la más importante de las cuales es porque realizar una limpieza adicional de la contraseña requiere un código adicional innecesario.

Argumentará (y lo verá en todas las publicaciones en las que se aceptan datos de usuario para su uso en sus sistemas) que deberíamos limpiar todas las entradas del usuario y que sería correcto para cualquier otra información que aceptemos de nuestros usuarios. Las contraseñas son diferentes. Las contraseñas hash no pueden ofrecer ninguna amenaza de inyección SQL porque la cadena se convierte en hash antes de almacenarla en la base de datos.

El acto de hash de una contraseña es el acto de hacer que la contraseña sea segura para almacenar en su base de datos. La función hash no le da un significado especial a ningún byte, por lo que no se requiere limpiar su entrada por razones de seguridad

Si sigue los mantras de permitir que los usuarios usen las contraseñas / frases que deseen y no limita las contraseñas , permitir cualquier longitud, cualquier cantidad de espacios y cualquier carácter especial hash hará que la contraseña / frase de contraseña sea segura sin importar lo que contenga la contraseña. A partir de ahora, el hash más común (el predeterminado), PASSWORD_BCRYPTconvierte la contraseña en una cadena de 60 caracteres de ancho que contiene una sal aleatoria junto con la información de la contraseña hash y un costo (el costo algorítmico de crear el hash):

PASSWORD_BCRYPT se usa para crear nuevos hashes de contraseña usando el algoritmo CRYPT_BLOWFISH. Esto siempre dará como resultado un hash con el formato de cripta "$ 2y $", que siempre tiene 60 caracteres de ancho.

Los requisitos de espacio para almacenar el hash están sujetos a cambios a medida que se agregan diferentes métodos de hash a la función, por lo que siempre es mejor ampliar el tipo de columna para el hash almacenado, como VARCHAR(255)o TEXT.

Puede usar una consulta SQL completa como contraseña y se le aplicará un hash, lo que la hará no ejecutable por el motor SQL, por ejemplo,

SELECT * FROM `users`;

Podría ser hash para $2y$10$1tOKcWUWBW5gBka04tGMO.BH7gs/qjAHZsC5wyG0zmI2C.KgaqU5G

Veamos cómo los diferentes métodos de desinfección afectan la contraseña:

La contraseña es I'm a "dessert topping" & a <floor wax>!(hay 5 espacios al final de la contraseña que no se muestran aquí).

Cuando aplicamos los siguientes métodos de recorte, obtenemos resultados muy diferentes:

var_dump(trim($_POST['upassword']));
var_dump(htmlentities($_POST['upassword']));
var_dump(htmlspecialchars($_POST['upassword']));
var_dump(addslashes($_POST['upassword']));
var_dump(strip_tags($_POST['upassword']));

Resultados:

string(40) "I'm a "dessert topping" & a <floor wax>!" // spaces at the end are missing
string(65) "I'm a &quot;dessert topping&quot; &amp; a &lt;floor wax&gt;!     " // double quotes, ampersand and braces have been changed
string(65) "I'm a &quot;dessert topping&quot; &amp; a &lt;floor wax&gt;!     " // same here
string(48) "I\'m a \"dessert topping\" & a <floor wax>!     " // escape characters have been added
string(34) "I'm a "dessert topping" & a !     " // looks like we have something missing

¿Qué sucede cuando los enviamos a password_hash()? Todos obtienen hash, tal como lo hizo la consulta anterior. El problema surge cuando intenta verificar la contraseña. Si empleamos uno o más de estos métodos, debemos volver a emplearlos antes de compararlos con password_verify(). Lo siguiente fallaría:

password_verify($_POST['upassword'], $hashed_password); // where $hashed_password comes from a database query

Tendría que ejecutar la contraseña publicada a través del método de limpieza que eligió antes de usar el resultado en la verificación de la contraseña. Es un conjunto de pasos innecesarios y no mejorará el hash.


¿Utiliza una versión de PHP menor a 5.5? Puede utilizar el password_hash() paquete de compatibilidad .

Realmente no debería usar hash de contraseña MD5 .

Jay Blanchard
fuente
13
No. Si creó su contraseña con espacios al final, lo cual está permitido, debe usarlos al iniciar sesión @DanBracuk
Jay Blanchard
12
¿Cómo es eso @DanBracuk? ¿Si permitimos que el usuario configure la contraseña que desee, incluidos los espacios iniciales / finales?
Jay Blanchard
16
Es por eso que la mayoría de las cosas requieren que ingrese la contraseña elegida dos veces. Si el usuario agregó los espacios por accidente, lo resolverán antes de continuar. Si el usuario lo hizo a propósito, no es un problema.
Luché con un oso una vez.
4
@MargaretBloom, una regla general es solo una heurística. A veces todavía necesitamos pensar en las cosas, como para las contraseñas. Dices "nadie sabe cómo cambiarán las cosas en el futuro", pero parece que si algo va a cambiar es la forma en que escapamos de los datos antes de ponerlos en la base de datos, en cuyo caso los usuarios se encontrarían bloqueados cuando sus contraseñas no coincidir con lo que hemos almacenado. ¿Cuál es el peligro de no escapar de los hash de contraseña frente al peligro de escapar de ellos?
DavidS
3
Exactamente: por supuesto, "escapará del hash" en el sentido limitado de pasarlo correctamente a una consulta SQL parametrizada, donde algún código en su conector SQL puede o no hacer algo con él que corresponda a "escapar", usted no ' Lo sé y no me importa. Simplemente no tendrá que escribir ningún código específico para lograr eso, porque es completamente rutinario para todas sus consultas SQL a menos que haya tomado algunas decisiones malas en la vida.
Steve Jessop
36

Antes de aplicar hash a la contraseña, debe normalizarla como se describe en la sección 4 de RFC 7613 . En particular:

  1. Regla de mapeo adicional: Cualquier instancia de espacio no ASCII DEBE mapearse con el espacio ASCII (U + 0020); un espacio no ASCII es cualquier punto de código Unicode que tenga una categoría general Unicode de "Zs" (con la excepción de U + 0020).

y:

  1. Regla de normalización: La forma C de normalización Unicode (NFC) DEBE aplicarse a todos los caracteres.

Esto intenta garantizar que si el usuario escribe la misma contraseña pero utilizando un método de entrada diferente, la contraseña aún debe aceptarse.

legoscia
fuente
3
@DavidS, un Mac Book norteamericano súper brillante (que Joe usó justo antes de irse) y una computadora taiwanesa de cibercafé mal internacionalizada (que Joe está tratando de usar para descargar su tarjeta de embarque de regreso).
Margaret Bloom
2
Suena patriotero. :-) Gracias.
DavidS
3
Hmm. Si hace esto, también debe validar las contraseñas para rechazar las que contengan caracteres aún sin asignar. Sería terrible si un usuario usa NEWFANGLED SPACE, que su aplicación no reconoce y, por lo tanto, realiza un hash tal como está, y luego actualiza su base de datos de caracteres Unicode y, de repente, NEWFANGLED SPACE se asigna a SPACE antes del hash, de modo que Ya no puede ingresar una contraseña que su aplicación convertirá en el hash anterior.
ruakh
4
@JayBlanchard Porque cuando presiona una barra espaciadora en una máquina y cuando la presiona en otra máquina, puede obtener dos puntos de código Unicode diferentes, y tendrán dos codificaciones UTF-8 diferentes, sin que el usuario se dé cuenta de nada. Se podría argumentar que este es un problema que desea ignorar, pero RFC 7613 surgió de problemas de la vida real, no es una recomendación para hacer que funcione.
Reincorporación a Monica
1
@ruakh Una vez que decida manejar las contraseñas de cierta manera, deben seguir manejándose de esa manera, o de lo contrario las cosas se romperán para los casos de uso existentes. Si tiene la intención de cambiar el método de preprocesamiento en el futuro, debe almacenarlo junto con la representación preprocesada y con hash de la contraseña. De esa manera, una vez que recibe la entrada, selecciona el método de preprocesamiento / hash en función de lo que está comparando.
Reincorporación a Monica