2010-01-09 14 views
16

Estoy creando un sitio web PHP que involucra a los usuarios que se registran, y me pregunto cuáles son las mejores prácticas para los códigos de "confirmación de correo electrónico".Mejores prácticas para los códigos de confirmación de correo electrónico

Los nuevos usuarios deben confirmar sus direcciones de correo electrónico. Esto lo hago generando un código y enviándolo al usuario en un correo electrónico, que luego puede usar para activar su cuenta. En lugar de almacenar esta clave en una base de datos, estoy usando un poco de solución práctica: el código es el resultado de:

md5("xxxxxxxxx".$username."xxxxxxxxxxx".$timestamp."xxxxxxxxx"); 

Donde $ sello de tiempo se refiere al tiempo fácil de creación. En general, estaba bastante satisfecho con esto, pero luego pensé: ¿es esto lo suficientemente seguro? ¿Y la posibilidad de colisiones? Y también necesito generar códigos para el restablecimiento de contraseñas, etc. Si utilicé una metodología similar, una colisión podría ocasionar que un usuario reinicie inadvertidamente la contraseña de otro usuario. Y eso no está bien.

Entonces, ¿cómo haces estas cosas? Mi pensamiento era una tabla con el siguiente formato:

codePK (int, a-I), userID (int), type (int), code (varchar 32), date (timestamp) 

Donde 'tipo' sería de 1, 2 o 3 significado de "activación", "cambio de correo electrónico" o "restablecer contraseña". ¿Es esta una buena forma de hacerlo? ¿Tienes una mejor manera?

Usando un método similar al anterior, ¿podría eliminar automáticamente cualquier cosa que tenga más de dos días de antigüedad sin usar cron-jobs? Mi anfitrión (almostfreespeech.net) no los admite. Si es posible, me gustaría evitar tener un cron-job en un host externo que wget es un script que borra cosas, ya que eso es simplemente desordenado = P.

Gracias!
Mala

Actualización:
Para aclarar: Me he dado cuenta de la única manera de ir de forma segura acerca de esta tarea es mediante el uso de una base de datos, que es lo que la función original estaba tratando de evitar. Mi pregunta es cómo debería estructurarse la tabla (¿o las tablas?). Alguien sugirió que elimine el código PK y simplemente haga que el código sea PK. Entonces, en resumen, mi pregunta es: ¿es esto lo que haces?

+0

¿La mejor práctica para los códigos de confirmación del correo electrónico? Déjelos fuera ... – Leo

+1

Es importante que los usuarios tengan una dirección de correo electrónico válida en el archivo, aunque solo sea para restablecer la contraseña. Si bien uno podría decir "bueno, esa es la responsabilidad del usuario" este es el mundo real con el que nos enfrentamos ... En cuanto el primer chico que "NUNCA olvidará su contraseña" olvide su contraseña, probablemente una o dos semanas después, mi sistema será "el más tonto de todos". – Mala

Respuesta

10

Cuando necesito este tipo de trucos que normalmente es de una de dos razones, ambos mencionados por usted:

  1. Como clave utilizada para mensajes de correo electrónico de verificación se envían al usuario
  2. Como clave utilizada para enlaces de restablecimiento de contraseña

Por supuesto, habría muchas otras ocasiones en las que consideraría utilizar una construcción de este tipo.

Antes que nada, siempre debes usar algún tipo de sal que esté oculta y que solo tú conozcas. Tenga en cuenta que esta sal debe ser diferente para cada usuario. La sal podría, por ejemplo, calcularse como sha256(something random). Esta sal se debe almacenar en la base de datos junto con el nombre de usuario y la contraseña (hash con la sal).

Lo que haría al enviar el enlace de restablecimiento de contraseña es crear otra sal (no le dé acceso al usuario a nada hash con su sal. Conoce su contraseña, por lo que con fuerza bruta podría deducir su sal) . La otra sal, que esencialmente es solo un hash de una cadena aleatoria (es posible que desee ir a md5 aquí, como mencionó que la longitud es un problema), en caso de que guarde en su base de datos.

A menudo, puede salirse con la suya agregando una columna adicional a su tabla de usuarios. Esto, sin embargo, también tiene algunos problemas, principalmente que una vez que la contraseña se ha restablecido o el usuario ha sido activado, eliminará la clave de la base de datos, lo que resulta en que la mayoría de las filas tienen valores nulos, lo que a su vez trae problemas .

Lo que esto esencialmente se reduce a:

  • Hash contraseñas de los usuarios usando una sal única-para-el-usuario ( y quizá una sal mundial, secreta).
  • Genere una clave mezclando varias fuentes aleatorias o pseudoaleatorias, como timestamps, mt_rand() o incluso random.org si realmente quiere cosas aleatorias.
  • Nunca use su sal global o la sal que es única para el usuario de hash cualquier cosa que el usuario tiene acceso a, incluyendo teclas de restablecimiento de contraseñas, claves de activación, etc.

Por favor, no es que yo soy de ninguna significa un experto en seguridad, y probablemente he olvidado varias cosas, y es posible que haya mencionado algunas prácticas muy malas. Sólo mis 5 centavos ;-)

1

¡Estaba lo suficientemente seguro hasta el momento en que publicaste tu método en Internet! Esto se debe a que confiabas en la seguridad por oscuridad, lo cual no es una buena idea.

Debería utilizar algún tipo de función hash con clave o MAC ideal, que incorpora una clave secreta que solo usted conoce.

+2

obviamente las 'x' no son 'x' en el código – Mala

+0

En ese caso, ha creado una función hash con clave y listo. Probablemente debería haberlo adivinado ...;) –

+0

;) lo siento, pensé que había especificado eso, pero al parecer edité el bit donde lo dije explícitamente ... en cualquier caso, me pregunto si la estructura de la tabla/usando una sola tabla para todos los códigos es apropiada – Mala

0

¿Por qué no hacer que el campo de código tenga un índice único? Entonces, ¿nunca habrá colisiones?

Además, si no es necesario para crear hash a partir de la entrada del usuario y hacerla coincidir con la base de datos de hash (confirmación por correo electrónico, contraseña restablecer etc) - se puede agregar una cadena aleatoria al cuerpo de hash, como md5('xxx'.$username.'xxx'.time().'xxx'.rand())

+0

, entonces no podría verificarla sin utilizar una base de datos; el objetivo de esa función era evitar su uso.Si voy a usar una base de datos de todos modos, entonces puedo dejar que toda la cadena sea completamente aleatoria – Mala

2

¿Por qué usar cualquiera de los datos del usuario como base para la clave de autorización?

Supongo que está almacenando los datos desactivados en una base de datos así que ¿por qué no simplemente agrega un registro adicional que es simplemente una clave aleatoria (tal vez un md5'ed uniqid con alguna manipulación adicional) y luego verifica eso?

+0

Originalmente estaba tratando de evitar tener que usar una base de datos para esto - de ahí el uso userdata – Mala

+1

Para ser honesto, no puedo Ayuda, pero piensa que una base de datos es el camino a seguir, de lo contrario vas a manipular/manipular un conjunto bastante limitado de datos estáticos. –

0

¿Por qué no pedirle al usuario que ingrese su nombre de usuario y su código, eliminando así cualquier problema de colisión?No pierde nada en cuanto a la seguridad, ya que todavía está solicitando la clave que recibiría del correo electrónico, pero evitaría que pudieran restablecer las contraseñas de otros usuarios.

+1

Idealmente, podrían simplemente hacer clic en un enlace en un correo electrónico. No quiero que esto sea horrendomente largo = P – Mala

Cuestiones relacionadas