Hash y sal seguros para contraseñas PHP

1174

Actualmente se dice que MD5 es parcialmente inseguro. Teniendo esto en cuenta, me gustaría saber qué mecanismo utilizar para la protección con contraseña.

Esta pregunta, ¿es "doble hashing" una contraseña menos segura que simplemente hacerlo una vez? sugiere que el hashing varias veces puede ser una buena idea, mientras que ¿Cómo implementar la protección con contraseña para archivos individuales? sugiere usar sal.

Estoy usando PHP Quiero un sistema de cifrado de contraseña rápido y seguro. Hackear una contraseña un millón de veces puede ser más seguro, pero también más lento. ¿Cómo lograr un buen equilibrio entre velocidad y seguridad? Además, preferiría que el resultado tenga un número constante de caracteres.

  1. El mecanismo de hash debe estar disponible en PHP
  2. Debe ser seguro
  3. Puede usar sal (en este caso, ¿son todas las sales igualmente buenas? ¿Hay alguna forma de generar buenas sales?)

Además, ¿debo almacenar dos campos en la base de datos (uno con MD5 y otro con SHA, por ejemplo)? ¿Lo haría más seguro o inseguro?

En caso de que no estuviera lo suficientemente claro, quiero saber qué funciones de hashing usar y cómo elegir una buena sal para tener un mecanismo de protección de contraseña rápido y seguro.

Preguntas relacionadas que no cubren mi pregunta:

¿Cuál es la diferencia entre SHA y MD5 en PHP?
Simple Password Encryption
Métodos seguros de almacenamiento de claves, contraseñas para asp.net
¿Cómo implementaría contraseñas saladas en Tomcat 5.5?

luiscubal
fuente
13
openwall.com/phpass también es muy buena biblioteca
Alfred
51
Md5 ahora es completamente inseguro
JqueryToAddNumbers
3
@NSAwesomeGuy Eso depende de para qué lo estés usando. Es trivial hacer coincidir con el arco iris o solo contraseñas MD5 sin sal de fuerza bruta, claro, pero con una salina decente todavía es extremadamente poco práctico construir una tabla de arcoíris para descifrar rápidamente los conjuntos de contraseñas, y la fuerza bruta no es un obstáculo.
Craig Ringer el
12
PHP 5.5+ tiene un hash de contraseña seguro integrado en php.net/manual/en/function.password-hash.php
Terence Johnson el

Respuestas:

982

DESCARGO DE RESPONSABILIDAD : Esta respuesta fue escrita en 2008.

Desde entonces, PHP nos ha dado password_hashy password_verify, y, desde su introducción, que son la clave hash recomendada y método de comprobación.

Sin embargo, la teoría de la respuesta sigue siendo una buena lectura.

TL; DR

No hacer

  • No limite los caracteres que los usuarios pueden ingresar para las contraseñas. Solo los idiotas hacen esto.
  • No limite la longitud de una contraseña. Si sus usuarios quieren una oración con supercalifragilisticexpialidocious, no les impida usarla.
  • No elimine ni escape HTML y caracteres especiales en la contraseña.
  • Nunca almacene la contraseña de su usuario en texto plano.
  • Nunca envíe una contraseña por correo electrónico a su usuario, excepto cuando haya perdido la suya, y usted haya enviado una temporal.
  • Nunca, nunca registre contraseñas de ninguna manera.
  • ¡Nunca hash contraseñas con SHA1 o MD5 o incluso SHA256! Las galletas modernas pueden superar los 60 y 180 mil millones de hashes / segundo (respectivamente).
  • No mezcle bcrypt y con la salida sin formato de hash () , use la salida hexadecimal o base64_encode. (Esto se aplica a cualquier entrada que pueda tener un pícaro \0, lo que puede debilitar seriamente la seguridad).

Dos

  • Usa scrypt cuando puedas; bcrypt si no puedes.
  • Use PBKDF2 si no puede usar bcrypt o scrypt, con hash SHA2.
  • Restablezca las contraseñas de todos cuando la base de datos se vea comprometida.
  • Implemente una longitud mínima razonable de 8-10 caracteres, además requiera al menos 1 letra mayúscula, 1 letra minúscula, un número y un símbolo. Esto mejorará la entropía de la contraseña, lo que a su vez hará que sea más difícil de descifrar. (Consulte la sección "¿Qué hace una buena contraseña?" Para un debate).

¿Por qué las contraseñas hash de todos modos?

El objetivo detrás de las contraseñas hash es simple: evitar el acceso malicioso a las cuentas de los usuarios al comprometer la base de datos. Entonces, el objetivo del hash de contraseñas es disuadir a un pirata informático o pirata informático al costarles demasiado tiempo o dinero para calcular las contraseñas de texto sin formato. Y el tiempo / costo son los mejores elementos de disuasión en su arsenal.

Otra razón por la que desea un hash bueno y robusto en las cuentas de un usuario es para darle tiempo suficiente para cambiar todas las contraseñas en el sistema. Si su base de datos se ve comprometida, necesitará tiempo suficiente para al menos bloquear el sistema, si no cambia cada contraseña en la base de datos.

Jeremiah Grossman, CTO de Whitehat Security, declaró en el blog de White Hat Security después de una reciente recuperación de contraseña que requirió la ruptura de su protección de contraseña por fuerza bruta:

Curiosamente, al vivir esta pesadilla, aprendí MUCHO que no sabía sobre el descifrado de contraseñas, el almacenamiento y la complejidad. He llegado a apreciar por qué el almacenamiento de contraseñas es mucho más importante que la complejidad de las contraseñas. Si no sabe cómo se almacena su contraseña, entonces todo de lo que realmente puede depender es de la complejidad. Esto podría ser de conocimiento común para los profesionales de contraseñas y criptografía, pero para el experto promedio de InfoSec o Web Security, lo dudo mucho.

(El énfasis es mío).

¿Qué hace una buena contraseña de todos modos?

Entropía . (No es que me suscriba completamente al punto de vista de Randall).

En resumen, la entropía es la cantidad de variación dentro de la contraseña. Cuando una contraseña solo tiene letras romanas en minúsculas, solo tiene 26 caracteres. Eso no es mucha variación. Las contraseñas alfanuméricas son mejores, con 36 caracteres. Pero permitir mayúsculas y minúsculas, con símbolos, tiene aproximadamente 96 caracteres. Eso es mucho mejor que solo letras. Un problema es que, para que nuestras contraseñas sean memorables, insertamos patrones, lo que reduce la entropía. ¡Uy!

La entropía de la contraseña se aproxima fácilmente. El uso de la gama completa de caracteres ascii (aproximadamente 96 caracteres que se pueden escribir) produce una entropía de 6.6 por carácter, que con 8 caracteres para una contraseña sigue siendo demasiado baja (52.679 bits de entropía) para seguridad futura. Pero la buena noticia es que las contraseñas más largas y las contraseñas con caracteres Unicode realmente aumentan la entropía de una contraseña y hacen que sea más difícil descifrarla.

Hay una discusión más larga sobre la entropía de contraseñas en el sitio Crypto StackExchange . Una buena búsqueda en Google también arrojará muchos resultados.

En los comentarios que hablé con @popnoodles, quienes señalaron que hacer cumplir una política de contraseña de longitud X con X muchas letras, números, símbolos, etc., en realidad puede reducir la entropía al hacer que el esquema de contraseña sea más predecible. Estoy de acuerdo. La aleatoriedad, lo más aleatoria posible, es siempre la solución más segura pero menos memorable.

Hasta donde he podido decir, crear la mejor contraseña del mundo es un Catch-22. No es memorable, demasiado predecible, demasiado corto, demasiados caracteres Unicode (difícil de escribir en un dispositivo Windows / Mobile), demasiado largo, etc. Ninguna contraseña es lo suficientemente buena para nuestros propósitos, por lo que debemos protegerlos como si fueran estaban en Fort Knox.

Mejores prácticas

Bcrypt y scrypt son las mejores prácticas actuales. Scrypt será mejor que bcrypt a tiempo, pero no ha visto la adopción como estándar por Linux / Unix o por servidores web, y aún no ha publicado revisiones en profundidad de su algoritmo. Pero aún así, el futuro del algoritmo parece prometedor. Si está trabajando con Ruby, hay una gema de scrypt que lo ayudará, y Node.js ahora tiene su propio paquete de scrypt . Puede usar Scrypt en PHP a través de la extensión Scrypt o la extensión Libsodium (ambos están disponibles en PECL).

Le recomiendo leer la documentación de la función cripta si desea comprender cómo usar bcrypt, o encontrar un buen contenedor o usar algo como PHPASS para una implementación más heredada. Recomiendo un mínimo de 12 rondas de bcrypt, si no de 15 a 18.

Cambié de opinión sobre el uso de bcrypt cuando supe que bcrypt solo usa el programa clave de blowfish, con un mecanismo de costo variable. Este último le permite aumentar el costo de la fuerza bruta de una contraseña al aumentar la ya costosa programación clave de blowfish.

Prácticas promedio

Ya casi no puedo imaginar esta situación. PHPASS es compatible con PHP 3.0.18 a 5.3, por lo que es utilizable en casi todas las instalaciones imaginables, y debe usarse si no sabe con certeza que su entorno es compatible con bcrypt.

Pero suponga que no puede usar bcrypt o PHPASS en absoluto. ¿Entonces que?

Pruebe una implementación de PDKBF2 con el número máximo de rondas que su entorno / aplicación / percepción del usuario puede tolerar. El número más bajo que recomendaría es de 2500 rondas. Además, asegúrese de usar hash_hmac () si está disponible para dificultar la reproducción de la operación.

Prácticas futuras

Próximamente en PHP 5.5 es una biblioteca de protección de contraseña completa que abstrae cualquier dolor de trabajo con bcrypt. Si bien la mayoría de nosotros estamos atrapados con PHP 5.2 y 5.3 en los entornos más comunes, especialmente los hosts compartidos, @ircmaxell ha creado una capa de compatibilidad para la próxima API que es compatible con PHP 5.3.7.

Resumen de criptografía y descargo de responsabilidad

La potencia de cálculo requerida para descifrar una contraseña hash no existe. La única forma en que las computadoras pueden "descifrar" una contraseña es recrearla y simular el algoritmo de hash utilizado para protegerla. La velocidad del hash está relacionada linealmente con su capacidad de ser forzado por la fuerza bruta. Peor aún, la mayoría de los algoritmos hash se pueden paralelizar fácilmente para funcionar aún más rápido. Es por eso que los esquemas costosos como bcrypt y scrypt son tan importantes.

No es posible prever todas las amenazas o vías de ataque, por lo que debe hacer su mejor esfuerzo para proteger a sus usuarios por adelantado . Si no lo hace, incluso podría perderse el hecho de que fue atacado hasta que sea demasiado tarde ... y usted es responsable . Para evitar esa situación, actúa paranoico para empezar. Ataque su propio software (internamente) e intente robar credenciales de usuario, o modificar las cuentas de otros usuarios o acceder a sus datos. Si no prueba la seguridad de su sistema, entonces no puede culpar a nadie más que a usted mismo.

Por último: no soy un criptógrafo. Lo que he dicho es mi opinión, pero creo que se basa en el buen sentido común ... y en mucha lectura. Recuerde, sea lo más paranoico posible, haga las cosas tan difíciles de entrometerse como sea posible, y luego, si todavía está preocupado, comuníquese con un hacker de sombrero blanco o un criptógrafo para ver qué dicen sobre su código / sistema.

Robert K
fuente
99
un secreto no ayuda, ya que se supone que su contraseña DB es secreta de todos modos; si pueden obtener esa base de datos, también pueden encontrar cualquier secreto que esté utilizando. Sin embargo, es importante que la sal sea aleatoria.
frankodwyer
2
Tenga en cuenta que no es realmente cierto que "el poder computacional para descifrar" todavía no existe. Como la mayoría de las contraseñas son palabras del diccionario o derivadas del diccionario, un ataque basado en el diccionario suele ser muy efectivo (de ahí el uso de políticas de contraseña y recuentos de iteraciones).
frankodwyer
66
Pulga @wicked, no estoy discutiendo contigo. Simplemente señalando cuán enrevesada y compleja es esta área de nuestro trabajo. Sigo esperando que me eduquen con la mejor y más inteligente práctica para configurar un sistema de gestión de contenido de un sitio web pequeño. Todavía estoy aprendiendo aquí. ... cada vez que leo algo que tiene sentido, pronto noto otras 5 publicaciones que lo contradicen. esa vuelta y vuelta se
marea
44
Interesante revisión. ¿Es la ID de usuario (digamos, un incremento automático BIGINT) un buen nonce? ¿O como no es aleatorio, no es bueno? Además, tendré que almacenar el nonce para cada usuario en la base de datos ... ¿La clave del sitio + nonce + HMAC proporciona una seguridad significativamente mejorada sobre un hash salado (con ID de usuario) iterado varias veces? Del mismo modo, ¿iterar HMAC varias veces es bueno para la seguridad?
luiscubal
44
Enviar una contraseña temporal por correo electrónico que requiere que el usuario la cambie la primera vez que la usa y enviar un enlace "seguro" por correo electrónico que les permita establecer su contraseña son igualmente riesgosos. En cualquier caso, cualquier persona que intercepte el correo electrónico puede acceder a la cuenta siempre que use el enlace o la contraseña antes de que lo haga el destinatario.
Tim Gautier
138

Una respuesta mucho más corta y segura: no escriba su propio mecanismo de contraseña , use un mecanismo probado.

  • PHP 5.5 o superior: password_hash () es de buena calidad y forma parte del núcleo de PHP.
  • Versiones PHP más antiguas: la biblioteca phpass de OpenWall es mucho mejor que la mayoría de los códigos personalizados, utilizados en WordPress, Drupal, etc.

La mayoría de los programadores simplemente no tienen la experiencia para escribir código criptográfico de forma segura sin introducir vulnerabilidades.

Autocomprobación rápida: ¿qué es el estiramiento de contraseña y cuántas iteraciones debe usar? Si no sabe la respuesta, debe usarla password_hash(), ya que el estiramiento de contraseña es ahora una característica crítica de los mecanismos de contraseña debido a CPU mucho más rápidas y al uso de GPU y FPGA para descifrar contraseñas a tasas de miles de millones de conjeturas por segundo (con GPU )

Por ejemplo, puede descifrar todas las contraseñas de Windows de 8 caracteres en 6 horas usando 25 GPU instaladas en 5 PC de escritorio. Esto es una fuerza bruta, es decir, enumerar y verificar cada contraseña de Windows de 8 caracteres , incluidos los caracteres especiales, y no es un ataque de diccionario. Eso fue en 2012, a partir de 2018, podría usar menos GPU o agrietarse más rápido con 25 GPU.

También hay muchos ataques de tabla de arco iris en las contraseñas de Windows que se ejecutan en CPU normales y son muy rápidos. Todo esto se debe a que Windows todavía no elimina ni estira sus contraseñas, incluso en Windows 10 : ¡no cometa el mismo error que Microsoft!

Ver también:

  • Excelente respuesta con más sobre por qué password_hash()o phpassson la mejor manera de hacerlo.
  • buen artículo de blog que ofrece "factores de trabajo" recomendados (número de iteraciones) para algoritmos principales, incluidos bcrypt, scrypt y PBKDF2.
RichVel
fuente
1
pero estos sistemas son más conocidos y tal vez ya están comprometidos. pero es mejor hacer el tuyo cuando no sabes lo que estás haciendo.
JqueryToAddNumbers
14
Re "estos sistemas son más conocidos y tal vez ya están comprometidos": no hay ninguna razón por la cual un sistema bien diseñado para la autenticación debería estar "ya comprometido" simplemente porque es mejor conocido. Las bibliotecas como phpass están escritas por expertos y revisadas por muchas personas en detalle: el hecho de que sean bien conocidas va junto con la revisión detallada de diferentes personas y es más probable que signifique que sean seguras.
RichVel
Teniendo en cuenta los recientes volcados de contraseña de LinkedIn, Last.fm y otros, esto es bastante actual. ¡Estás en buena compañía al no saber cómo escribir tu propio mecanismo de contraseña!
RichVel
3
@PP: en mi opinión, las posibilidades de que un algoritmo de hash de contraseña revisado por pares tenga una puerta trasera NSA son muy bajas. Las posibilidades de que alguien que no sea un experto en criptografía real escriba un nuevo mecanismo de hash de contraseña sin otras vulnerabilidades es mucho menor. Y la aplicación web típica usa solo hash MD5 o SHA-1, lo cual es terrible, incluso el excelente libro Essential PHP Security de Chris Shiflett recomienda MD5 ...
RichVel
1
@RichVel - La función password_hash (). Como se mencionó anteriormente, está integrado en el núcleo de PHP (también conocido como / ext / standard).
CubicleSoft
43

No almacenaría el hash de contraseña de dos maneras diferentes, porque entonces el sistema es al menos tan débil como el más débil de los algoritmos de hash en uso.

Tom Haigh
fuente
no para el hashing de contraseñas. el atacante solo necesita romper un hash para recuperar la contraseña. el punto es discutible de todos modos, ya que ni MD5 ni SHA1 tienen interrupciones prácticas disponibles en el escenario de contraseña.
frankodwyer
2
lo siento, leí mal su respuesta al recomendar el uso de dos hashes ... de hecho está en lo correcto. El uso de dos hashes debilita el sistema en el caso de la contraseña, ya que solo necesitan romper el hash más débil.
frankodwyer
40

A partir de PHP 5.5, PHP tiene funciones simples y seguras para cifrar y verificar contraseñas, password_hash () y password_verify ()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

Cuando password_hash()se usa, genera una sal aleatoria y la incluye en el hash de salida (junto con el costo y el algoritmo utilizados), password_verify()luego lee ese hash y determina el método de sal y cifrado utilizado, y lo verifica con la contraseña de texto sin formato proporcionada.

Proporcionar PASSWORD_DEFAULTinstrucciones a PHP para que use el algoritmo de hash predeterminado de la versión instalada de PHP. Exactamente qué algoritmo significa que está destinado a cambiar con el tiempo en futuras versiones, por lo que siempre será uno de los algoritmos más fuertes disponibles.

El aumento del costo (que por defecto es 10) hace que el hash sea más difícil de aplicar por fuerza bruta, pero también significa que generar hash y verificar contraseñas en su contra será más trabajo para la CPU de su servidor.

Tenga en cuenta que aunque el algoritmo de hash predeterminado puede cambiar, los hashes antiguos continuarán verificándose bien porque el algoritmo utilizado se almacena en el hash y lo password_verify()retoma.

AliteradoAlicia
fuente
33

Aunque la pregunta ha sido respondida, solo quiero reiterar que las sales utilizadas para el hash deben ser aleatorias y no como la dirección de correo electrónico como se sugiere en la primera respuesta.

Hay más explicaciones disponibles en: http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

Recientemente tuve una discusión acerca de si los hashes de contraseñas salados con bits aleatorios son más seguros que los salados con sales adivinables o conocidas. Veamos: si el sistema que almacena la contraseña se ve comprometido y el sistema que almacena la sal aleatoria, el atacante tendrá acceso a hash y sal, por lo que no importa si la sal es aleatoria o no. El atacante puede generar tablas de arco iris precalculadas para descifrar el hash. Aquí viene la parte interesante: no es tan trivial generar tablas precalculadas. Tomemos un ejemplo del modelo de seguridad WPA. Su contraseña WPA nunca se envía al punto de acceso inalámbrico. En cambio, está codificado con su SSID (el nombre de la red como Linksys, Dlink, etc.). Una muy buena explicación de cómo funciona esto está aquí. Para recuperar la contraseña del hash, Necesitará conocer la contraseña y la sal (nombre de la red). Church of Wifi ya ha precalculado las tablas hash que tienen los mejores 1000 SSID y aproximadamente 1 millón de contraseñas. El tamaño de todas las tablas es de unos 40 GB. Como puede leer en su sitio, alguien usó 15 matrices FGPA durante 3 días para generar estas tablas. Suponiendo que la víctima esté usando el SSID como "a387csf3" y la contraseña como "123456", ¿serán descifradas por esas tablas? ¡No! .. no puede. Incluso si la contraseña es débil, las tablas no tienen hashes para SSID a387csf3. Esta es la belleza de tener sal al azar. Disuadirá a los crackers que prosperan en tablas precalculadas. ¿Puede detener a un hacker determinado? Probablemente no. Pero el uso de sales aleatorias proporciona una capa adicional de defensa. Mientras estamos en este tema, Discutamos la ventaja adicional de almacenar sales aleatorias en un sistema separado. Escenario n. ° 1: los valores hash de contraseña se almacenan en el sistema X y los valores de sal utilizados para el hash se almacenan en el sistema Y. Estos valores de sal son adivinables o conocidos (por ejemplo, nombre de usuario) Escenario n. ° 2: los valores hash de contraseña se almacenan en el sistema X y los valores de sal utilizados para el hash se almacena en el sistema Y. Estos valores de sal son aleatorios. En caso de que el sistema X se haya visto comprometido, como puede adivinar, existe una gran ventaja de usar sal aleatoria en un sistema separado (Escenario # 2). El atacante deberá adivinar los valores de suma para poder descifrar los hash. Si se usa una sal de 32 bits, se requerirán 2 ^ 32 = 4,294,967,296 (aproximadamente 4,2 mil millones) de iteraciones para cada contraseña adivinada. Los valores de contraseña se almacenan en el sistema X y los valores de sal utilizados para el hash se almacenan en el sistema Y. Estos valores de sal son adivinables o conocidos (por ejemplo, nombre de usuario) Escenario # 2: Los valores de contraseña se almacenan en el sistema X y los valores de sal utilizados para el hash se almacenan en sistema Y. Estos valores de sal son aleatorios. En caso de que el sistema X se haya visto comprometido, como puede adivinar, existe una gran ventaja de usar sal aleatoria en un sistema separado (Escenario # 2). El atacante deberá adivinar los valores de suma para poder descifrar los hash. Si se usa una sal de 32 bits, se requerirán 2 ^ 32 = 4,294,967,296 (aproximadamente 4,2 mil millones) de iteraciones para cada contraseña adivinada. Los valores de contraseña se almacenan en el sistema X y los valores de sal utilizados para el hash se almacenan en el sistema Y. Estos valores de sal son adivinables o conocidos (por ejemplo, nombre de usuario) Escenario # 2: Los valores de contraseña se almacenan en el sistema X y los valores de sal utilizados para el hash se almacenan en sistema Y. Estos valores de sal son aleatorios. En caso de que el sistema X se haya visto comprometido, como puede adivinar, existe una gran ventaja de usar sal aleatoria en un sistema separado (Escenario # 2). El atacante deberá adivinar los valores de suma para poder descifrar los hash. Si se usa una sal de 32 bits, se requerirán 2 ^ 32 = 4,294,967,296 (aproximadamente 4,2 mil millones) de iteraciones para cada contraseña adivinada. Estos valores de sal son aleatorios. En caso de que el sistema X se haya visto comprometido, como puede adivinar, existe una gran ventaja de usar sal aleatoria en un sistema separado (Escenario # 2). El atacante deberá adivinar los valores de suma para poder descifrar los hash. Si se usa una sal de 32 bits, se necesitarán 2 ^ 32 = 4,294,967,296 (aproximadamente 4,2 mil millones) de iteraciones para cada contraseña adivinada. Estos valores de sal son aleatorios. En caso de que el sistema X se haya visto comprometido, como puede adivinar, existe una gran ventaja de usar sal aleatoria en un sistema separado (Escenario # 2). El atacante deberá adivinar los valores de suma para poder descifrar los hash. Si se usa una sal de 32 bits, se necesitarán 2 ^ 32 = 4,294,967,296 (aproximadamente 4,2 mil millones) de iteraciones para cada contraseña adivinada.

Gaurav Kumar
fuente
77
Incluso si el atacante obtiene la sal, una cadena "sitesalt: usersalt: password" sigue siendo resistente a las tablas precalculadas, ya que el atacante necesita generar las tablas para cada usuario (por lo que el ataque se vuelve mucho más lento), a menos, por supuesto, un usuario específico está siendo atacado ...
luiscubal
Con respecto a "Incluso si el atacante obtiene la sal, una cadena" sitesalt: usersalt: password "sigue siendo resistente a las tablas precalculadas", totalmente de acuerdo. Mi punto es que sitesalt, si se hace al azar y por mucho tiempo, hará que el sistema sea más seguro de lo que se puede predecir (sitesalt). He visto a algunas personas recomendar el uso de la identificación de correo electrónico, etc. como sal, y lo desaconsejo.
Gaurav Kumar
Te perdiste lo que escribí originalmente. Le dije que usara un nonce aleatorio, almacenado con el registro, MÁS la dirección de correo electrónico. La adición de la dirección de correo electrónico crea una entropía adicional para que el hacker pueda trabajar. Desde entonces he reescrito mi respuesta a favor de bcrypt.
Robert K
28

Solo quiero señalar que PHP 5.5 incluye una API de hash de contraseña que proporciona un contenedor crypt(). Esta API simplifica significativamente la tarea de hash, verificación y rehash hashes de contraseñas. El autor también ha lanzado un paquete de compatibilidad (en forma de un único archivo password.php que simplemente requiredebe usar), para aquellos que usan PHP 5.3.7 y versiones posteriores y desean usarlo ahora mismo.

Solo es compatible con BCRYPT por el momento, pero su objetivo es ampliarlo fácilmente para incluir otras técnicas de hash de contraseña y debido a que la técnica y el costo se almacenan como parte del hash, los cambios en su técnica / costo de hashing preferido no invalidarán los hashes actuales, el marco automáticamente, usará la técnica / costo correcto al validar. También se encarga de generar una sal "segura" si no define explícitamente la suya.

La API expone cuatro funciones:

  • password_get_info() - devuelve información sobre el hash dado
  • password_hash() - crea un hash de contraseña
  • password_needs_rehash()- comprueba si el hash dado coincide con las opciones dadas. Útil para verificar si el hash se ajusta a su esquema actual de técnica / costo, lo que le permite volver a repetir si es necesario
  • password_verify() - verifica que una contraseña coincida con un hash

Por el momento, estas funciones aceptan las constantes de contraseña PASSWORD_BCRYPT y PASSWORD_DEFAULT, que son sinónimos en este momento, la diferencia es que PASSWORD_DEFAULT "puede cambiar en versiones de PHP más nuevas cuando se admiten algoritmos de hash más nuevos y más fuertes". El uso de PASSWORD_DEFAULT y password_needs_rehash () al iniciar sesión (y volver a escribir si es necesario) debería garantizar que sus hashes sean razonablemente resistentes a los ataques de fuerza bruta con poco o ningún trabajo para usted.

EDITAR: Me acabo de dar cuenta de que esto se menciona brevemente en la respuesta de Robert K. Dejaré esta respuesta aquí, ya que creo que proporciona un poco más de información sobre cómo funciona y la facilidad de uso que proporciona para aquellos que no conocen la seguridad.

JonoCoetzee
fuente
19

Estoy usando Phpass, que es una clase PHP simple de un archivo que podría implementarse muy fácilmente en casi todos los proyectos PHP. Ver también El H .

Por defecto, utilizó el cifrado más fuerte disponible que se implementa en Phpass, que es bcrypty recurre a otros cifrados hasta MD5 para proporcionar compatibilidad con versiones anteriores de marcos como Wordpress.

El hash devuelto podría almacenarse en la base de datos tal como está. El uso de muestra para generar hash es:

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

Para verificar la contraseña, se puede usar:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);
rabudde
fuente
14

COSAS PARA RECORDAR

Se ha dicho mucho sobre el cifrado de contraseña para PHP, la mayoría de los cuales es un muy buen consejo, pero antes de comenzar el proceso de usar PHP para el cifrado de contraseña, asegúrese de tener lo siguiente implementado o listo para ser implementado.

SERVIDOR

PUERTOS

No importa qué tan buena sea su encriptación si no asegura adecuadamente el servidor que ejecuta PHP y DB, todos sus esfuerzos no valen nada. La mayoría de los servidores funcionan relativamente de la misma manera, tienen puertos asignados para permitirle acceder de forma remota a través de ftp o shell. Asegúrese de cambiar el puerto predeterminado de cualquier conexión remota que tenga activa. Al no hacer esto, en efecto, ha hecho que el atacante haga un paso menos para acceder a su sistema.

NOMBRE DE USUARIO

Por todo lo que es bueno en el mundo, no use el nombre de usuario admin, root o algo similar. Además, si está en un sistema basado en Unix, NO haga accesible el inicio de sesión de la cuenta raíz, siempre debe ser solo sudo.

CONTRASEÑA

Le dice a sus usuarios que hagan buenas contraseñas para evitar ser pirateados, hagan lo mismo. ¿Cuál es el punto de pasar por todo el esfuerzo de cerrar la puerta cuando tienes la puerta trasera abierta de par en par?

BASE DE DATOS

SERVIDOR

Lo ideal es que desee su base de datos y APLICACIÓN en servidores separados. Esto no siempre es posible debido al costo, pero permite cierta seguridad ya que el atacante tendrá que pasar por dos pasos para acceder completamente al sistema.

USUARIO

Siempre haga que su aplicación tenga su propia cuenta para acceder a la base de datos y solo otorgue los privilegios que necesitará.

Luego, tenga una cuenta de usuario separada para usted que no esté almacenada en ningún lugar del servidor, ni siquiera en la aplicación.

Como siempre, NO hagas esta raíz o algo similar.

CONTRASEÑA

Siga las mismas pautas que con todas las buenas contraseñas. Tampoco reutilice la misma contraseña en ninguna cuenta SERVER o DB en el mismo sistema.

PHP

CONTRASEÑA

NUNCA almacene una contraseña en su base de datos, en su lugar almacene el hash y la sal única, explicaré por qué más adelante.

HASHING

¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ de la misma manera y compara los dos hashes. Esto significa que, incluso si un atacante obtiene acceso a la base de datos, no sabe cuál es la contraseña, solo el hash resultante. Lo que significa más seguridad para sus usuarios en el peor escenario posible.

Hay un montón de buenas funciones hash por ahí ( password_hash, hash, etc ...), pero tiene que seleccionar un algoritmo de hash bueno para el que sea eficaz. (bcrypt y otros similares son algoritmos decentes).

Cuando la clave es la velocidad de hashing, cuanto más lenta, más resistente a los ataques de fuerza bruta.

Uno de los errores más comunes en el hash es que los hash no son exclusivos de los usuarios. Esto se debe principalmente a que las sales no se generan de manera única.

SALAZÓN

Las contraseñas siempre deben ser saladas antes del hash. Salar agrega una cadena aleatoria a la contraseña para que contraseñas similares no aparezcan igual en la base de datos. Sin embargo, si la sal no es exclusiva de cada usuario (es decir, usted usa una sal codificada), entonces prácticamente no la tiene. Porque una vez que un atacante descubre una contraseña de sal, tiene la sal para todos ellos.

Cuando cree una sal, asegúrese de que sea única para la contraseña que está salando, luego almacene tanto el hash como la sal completos en su base de datos. Lo que esto hará es lograr que un atacante tenga que romper individualmente cada sal y hachís antes de poder acceder. Esto significa mucho más trabajo y tiempo para el atacante.

USUARIOS QUE CREAN CONTRASEÑAS

Si el usuario está creando una contraseña a través de la interfaz, eso significa que debe enviarse al servidor. Esto abre un problema de seguridad porque eso significa que la contraseña no cifrada se está enviando al servidor y si un atacante puede escuchar y acceder, toda su seguridad en PHP no tiene valor. SIEMPRE transmita los datos SEGURAMENTE, esto se hace a través de SSL, pero tenga cuidado, incluso SSL no es perfecto (el defecto Heartbleed de OpenSSL es un ejemplo de esto).

También haga que el usuario cree una contraseña segura, es simple y siempre debe hacerse, el usuario se lo agradecerá al final.

Finalmente, sin importar las medidas de seguridad que tome, nada es 100% seguro, cuanto más avanzada sea la tecnología para proteger, más avanzados serán los ataques. Pero seguir estos pasos hará que su sitio sea más seguro y mucho menos deseable para los atacantes.

Aquí hay una clase PHP que crea un hash y salt para una contraseña fácilmente

http://git.io/mSJqpw

wmfrancia
fuente
1
Debería eliminar SHA512 de su lista de algoritmos hash decentes, porque es demasiado rápido. Úselo solo en combinación con PBKDF2. Si bien BCrypt se basa en blowfish, el mismo blowfish es un algoritmo para el cifrado, no para el hash.
martinstoeckli
1
¿Cómo se almacena la sal al azar en el DB? Creo que no lo hash (no se puede usar para la verificación) ni lo almacena en claro (no hay beneficios reales si el atacante puede leer la base de datos). Entonces, ¿cómo lo haces?
Iazel
wmfrancia escribió: "La salazón agrega una cadena aleatoria a la contraseña para que contraseñas similares no aparezcan igual en la base de datos". Esto no tiene sentido para mi. Los hashes en la base de datos ya aparecerán diferentes porque es una propiedad de las funciones hash.
H2ONaCl
wmfancia escribió con respecto a una sal constante: "una vez que un atacante descubre una contraseña de sal, tiene la sal para todos ellos". Lo mismo puede decirse que si el pirata informático descubre qué campo de DB es la sal, tiene las sales para todos ellos. Dado que una sal constante probablemente no estaría en la base de datos, eso es bueno acerca de una sal constante.
H2ONaCl
Por supuesto, estos comentarios no sugieren que una sal aleatoria por usuario no sea mejor que una sal por aplicación. Es mejor.
H2ONaCl
12

Google dice que SHA256 está disponible para PHP.

Definitivamente deberías usar una sal. Recomiendo usar bytes aleatorios (y no limitarse a caracteres y números). Como de costumbre, cuanto más tiempo elija, más seguro, más lento se vuelve. 64 bytes debería estar bien, supongo.

AticusFinch
fuente
13
¿64 bits deberían ser suficientes para cualquiera?
Konerak
@Konerak, volvería a esto después de 20 años. :) Pero sí SHA256 está disponible. Si desea saber qué tan seguro es SHA256, puede consultar esto: security.stackexchange.com/questions/90064/…
Vincent Edward Gedaria Binua
8

Al final, el doble hashing, matemáticamente, no proporciona ningún beneficio. En la práctica, sin embargo, es útil para prevenir ataques basados ​​en tablas de arco iris. En otras palabras, no es más beneficioso que el hashing con sal, que requiere mucho menos tiempo de procesador en su aplicación o en su servidor.

Max
fuente
2
el hashing múltiple también protege contra ataques de diccionario y fuerza bruta, es decir, simplemente hace que tarden más en calcularse.
frankodwyer
66
el doble hashing no le dará una ventaja significativa, pero las iteraciones de hashing multi-ronda siguen siendo una defensa factible contra el diccionario y los ataques de fuerza bruta. Los hashes de contraseñas de fuerza industrial usan más de 1000 rondas. El PBKDF1 de PKCS # 5 sugiere un mínimo de 1000 rondas.
Berk D. Demir el
8

Encontré un tema perfecto sobre este asunto aquí: https://crackstation.net/hashing-security.htm , quería que se beneficiara de él, aquí también está el código fuente que también proporcionó prevención contra ataques basados ​​en el tiempo.

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>
Jason OOO
fuente
Usted nos da una solución sin ningún uso sin uso
Michael
6

Por lo general, uso SHA1 y salt con la ID de usuario (o alguna otra información específica del usuario), y a veces también uso un salt constante (por lo que tengo 2 partes de la salt).

SHA1 ahora también se considera algo comprometido, pero en un grado mucho menor que MD5. Al usar una sal (cualquier sal), evitas el uso de una tabla de arco iris genérica para atacar tus hashes (algunas personas incluso han tenido éxito al usar Google como una especie de tabla de arco iris al buscar el hash). Un atacante podría generar una tabla de arco iris usando su sal, por eso debe incluir una sal específica del usuario. De esa manera, tendrán que generar una tabla de arcoíris para todos y cada uno de los registros de su sistema, ¡no solo uno para todo el sistema! Con ese tipo de salazón, incluso el MD5 es decentemente seguro.

rmeador
fuente
2
la sal constante no es una gran idea ... probablemente no sea un defecto fatal, pero debilita innecesariamente el esquema.
frankodwyer
MD5 y SHA1 son rápidos, por lo que esta es una mala dieta.
CodesInChaos
4

SHA1 y una sal deberían ser suficientes (dependiendo, naturalmente, de si está codificando algo para Fort Knox o un sistema de inicio de sesión para su lista de compras) en el futuro previsible. Si SHA1 no es lo suficientemente bueno para usted, use SHA256 .

La idea de una sal es desequilibrar los resultados del hash, por así decirlo. Se sabe, por ejemplo, que el hash MD5 de una cadena vacía es d41d8cd98f00b204e9800998ecf8427e. Entonces, si alguien con una memoria lo suficientemente buena vería ese hash y sabría que es el hash de una cadena vacía. Pero si la cadena está salada (por ejemplo, con la cadena " MY_PERSONAL_SALT"), el hash para la 'cadena vacía' (es decir, " MY_PERSONAL_SALT") se convierte aeac2612626724592271634fb14d3ea6, por lo tanto, no es obvio para rastrear. Lo que estoy tratando de decir, que es mejor usar cualquier sal, de no hacerlo. Por lo tanto, no es demasiado importante saber qué sal usar.

En realidad, hay sitios web que hacen exactamente esto : puede alimentarlo con un hash (md5), y escupe un texto sin formato conocido que genera ese hash en particular. Si tuviera acceso a una base de datos que almacena hash md5 simples, sería trivial que ingrese el hash para el administrador de dicho servicio e inicie sesión. Pero, si las contraseñas fueran saladas, dicho servicio se convertiría ineficaz.

Además, el doble hashing generalmente se considera un mal método, ya que disminuye el espacio resultante. Todos los hashes populares son de longitud fija. Por lo tanto, solo puede tener valores finitos de esta longitud fija, y los resultados se vuelven menos variados. Esto podría considerarse como otra forma de salazón, pero no lo recomendaría.

Henrik Paul
fuente
El sitio objetivo no debe contener nada demasiado sensible (no es un banco), pero aún así prefiero tenerlo asegurado.
luiscubal
1
el doble hashing no reduce el espacio resultante. El hashing iterativo es un control común contra los ataques de diccionario y fuerza bruta (los ralentiza mucho más de lo que ralentiza la comprobación de su contraseña).
frankodwyer
2
@frankodwyer: sí, es malo. sha1(sha1($foo))reduce efectivamente el espacio de salida, porque cualquier colisión de la función interna se convertirá automáticamente en una colisión de la externa. La degradación es lineal, pero sigue siendo una preocupación. Los métodos de hashing iterativos retroalimentan los datos en cada ronda, como $hash = sha1(sha1($salt . $password) . $salt). Pero eso todavía no es bueno ... Quédese con PBKDF2 o Bcrypt ...
ircmaxell
-7

ok en el ajuste necesitamos sal, la sal debe ser única, así que vamos a generarla

   /**
     * Generating string
     * @param $size
     * @return string
     */
    function Uniwur_string($size){
        $text = md5(uniqid(rand(), TRUE));
        RETURN substr($text, 0, $size);
    }

también necesitamos el hash que estoy usando sha512 es el mejor y está en php

   /**
     * Hashing string
     * @param $string
     * @return string
     */
    function hash($string){
        return hash('sha512', $string);
    }

así que ahora podemos usar estas funciones para generar una contraseña segura

// generating unique password
$password = Uniwur_string(20); // or you can add manual password
// generating 32 character salt
$salt = Uniwur_string(32);
// now we can manipulate this informations

// hashin salt for safe
$hash_salt = hash($salt);
// hashing password
$hash_psw = hash($password.$hash_salt);

ahora necesitamos guardar en la base de datos nuestro valor de variable $ hash_psw y $ variable de sal

y para autorizar usaremos los mismos pasos ...

Es la mejor manera de proteger las contraseñas de nuestros clientes ...

Ps para los últimos 2 pasos, puede usar su propio algoritmo ... pero asegúrese de que puede generar esta contraseña cifrada en el futuro cuando necesite autorizar al usuario ...

shalvasoft
fuente
44
Esta pregunta fue sobre hashes para contraseñas. Una ejecución de sha512(incluso si está salada) se considera ampliamente que no es lo suficientemente buena para la protección con contraseña. (también que RNG no es criptográficamente seguro, por lo que usarlo para generar contraseñas es arriesgado).
luiscubal
2
No tienes idea de lo que estás haciendo. Lea las principales respuestas en esta publicación y podrá ver por qué su código no solo es inseguro, sino que no tiene sentido.
críptico ツ
Okay. Mi código no es seguro. así que dime por qué estás usando en tus algoritmos ony sha256 ??? Sé que sha512 es el mejor ¿por qué no usarlo?
shalvasoft
1
@shalvasoft sha512 es bastante bueno para el hash de uso general, pero la protección con contraseña requiere hashes con propiedades muy específicas ("ser lento" es extrañamente bueno , por ejemplo, y sha512 es bastante rápido). Algunas personas han usado sha512 como un bloque de construcción para crear funciones de hashing de contraseñas, pero hoy en día el enfoque recomendado es "usar bcrypt y vigilar scrypt".
luiscubal