¿Cómo configurar la verificación de correo electrónico del usuario después del registro?

30 jun 2018, 11:00:23
Vistas: 26.2K
Votos: 10

¿Sabes cómo todos estos sitios web envían enlaces a sus nuevos usuarios para que verifiquen su dirección de correo electrónico? Estoy tratando de configurar algo así, pero después de investigar aún no he encontrado una buena explicación sobre cómo implementarlo.

Estoy abierto a recomendaciones de plugins, sin embargo, la mayoría de los plugins que encontré tienen una gran cantidad de otras características que realmente no necesito.

Sin usar un plugin, ¿cómo podría agregar esto a mi código?

Mi enfoque sería agregar un atributo 'Email no verificado' al meta del usuario después del registro y enviar un correo electrónico con algún tipo de clave de verificación al usuario. Sin embargo, ¿cómo puedo verificar si el usuario realmente hizo clic en ese enlace?

Gracias por cualquier consejo

0
Todas las respuestas a la pregunta 2
2
12

Puedes usar el hook user_register

add_action( 'user_register', 'my_registration', 10, 2 );
function my_registration( $user_id ) {
    // obtener datos del usuario
    $user_info = get_userdata($user_id);
    // crear código md5 para verificar después
    $code = md5(time());
    // convertirlo en un código para enviarlo al usuario por correo electrónico
    $string = array('id'=>$user_id, 'code'=>$code);
    // crear el código de activación y el estado de activación
    update_user_meta($user_id, 'account_activated', 0);
    update_user_meta($user_id, 'activation_code', $code);
    // crear la URL
    $url = get_site_url(). '/my-account/?act=' .base64_encode( serialize($string));
    // básicamente editaremos aquí para hacerlo más agradable
    $html = 'Por favor haz clic en los siguientes enlaces <br/><br/> <a href="'.$url.'">'.$url.'</a>';
    // enviar un correo electrónico al usuario
    wp_mail( $user_info->user_email, __('Asunto del Correo','text-domain') , $html);
}

Puedes verificar $_GET['act'] y luego activar si es una clave válida actualizando el valor meta account_activated. Puedes usar el hook wp_authenticate_user para verificar el estado de activación cada vez que el usuario intente iniciar sesión.

Fragmento para validar:

add_action( 'init', 'verify_user_code' );
function verify_user_code(){
    if(isset($_GET['act'])){
        $data = unserialize(base64_decode($_GET['act']));
        $code = get_user_meta($data['id'], 'activation_code', true);
        // verificar si el código proporcionado es el mismo que el nuestro
        if($code == $data['code']){
            // actualizar los metadatos del usuario
            update_user_meta($data['id'], 'is_activated', 1);
            wc_add_notice( __( '<strong>Éxito:</strong> ¡Tu cuenta ha sido activada! ', 'text-domain' )  );
        }
    }
}
30 jun 2018 12:22:54
Comentarios

¡Gracias, eso es realmente útil! Solo una cosa, ¿cómo puedo acceder al ID de Usuario y al $code desde la URL? Seguramente necesito decodificarlo de alguna manera, ¿no?

Philipp K Philipp K
30 jun 2018 12:53:41

Actualicé la respuesta :)

Akshat Akshat
30 jun 2018 13:01:22
10

Chicos, creo que el método anterior introduce vulnerabilidades de seguridad que no deberían estar ahí.

El objetivo principal de la verificación por correo electrónico es asegurarnos de que las personas que se registran proporcionen una dirección de correo real que les pertenezca o al menos a la que tengan acceso. No queremos que se registren con direcciones de correo aleatorias que pertenezcan a otros, por ejemplo, tu dirección de correo.

El código anterior tiene vulnerabilidades que podrían permitir a un hacker registrar una dirección de correo aleatoria que pertenezca a otra persona y luego adivinar relativamente fácil el valor de $user_id y el valor de $code en tu página de verificación de correo.

Primera vulnerabilidad

Estás usando $user_id. Sé que este valor podría ser cualquier cosa, pero típicamente será un entero, especialmente si usas WordPress, que es aproximadamente el 30% de los sitios web en internet, y viendo el código PHP anterior, efectivamente está basado en WordPress. El hacker obtendrá su $user_id como parte del proceso de registro o lo adivinará mediante fuerza bruta, simplemente incrementando secuencialmente comenzando en 1 y continuando con 2, 3, 4, 5, 6... Adivinarán su $user_id en menos de un día, quizás incluso en menos de una hora si tu sitio no tiene muchos miembros.

Segunda vulnerabilidad

Estás creando un $code usando la función de hash MD5 y la hora de registro. El hacker sabe a qué hora se registró. Digamos que el hacker se registra a las 3 pm. Ahora, todo lo que el hacker tiene que hacer es aplicar MD5 a horas desde las 2:55 pm hasta las 3:05 pm y adivinarán el $code en menos de una hora.

Mirando lo anterior, el hacker puede simplemente adivinar por fuerza bruta el $user_id y $code en menos de un día y verificar una dirección de correo que no le pertenece

tsk tsk tsk

Un enfoque mejor sería generar un $code con la función rand() usando mayúsculas (A-Z), minúsculas (a-z), números (0-9) y caracteres especiales como (!&#). Esa función MD5 solo usa números del 0-9 y letras minúsculas de la a-f, y la forma en que la están usando, basada en la hora de registro, la hace increíblemente fácil de reducir y atacar por fuerza bruta.

He escrito el siguiente código PHP para generar un $code aleatorio con mayúsculas, minúsculas, números y caracteres especiales. No se lo pongan tan fácil a los hackers, chicos.

function generateRandomString($stringLength){
    //especificar caracteres a usar en la generación de la cadena aleatoria, no incluir caracteres que WordPress no permita al crear usuarios.
    $characters = "0123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_[]{}!@$%^*().,>=-;|:?";

    //obtener la longitud total de los caracteres especificados para generar la cadena aleatoria
    $charactersLength = strlen($characters);

    //declarar una cadena que usaremos para crear la cadena aleatoria
    $randomString = '';

    for ($i = 0; $i < $stringLength; $i++) {
        //generar caracteres aleatorios
        $randomCharacter = $characters[rand(0, $charactersLength - 1)];
        //agregar los caracteres aleatorios a la cadena
        $randomString .=  $randomCharacter;
    };

    //sanitize_user, por si acaso
    $sanRandomString = sanitize_user($randomString);

    //verificar que la cadena contenga mayúsculas, minúsculas, números y caracteres especiales, y que tenga la longitud correcta
    if ( (preg_match('([a-zA-Z].*[0-9]|[0-9].*[a-zA-Z].*[_\W])', $sanRandomString)==1) && (strlen($sanRandomString)==$stringLength) )
    {
        //devolver la cadena si cumple con los criterios de complejidad
        return $sanRandomString;
    } else {
        //si la cadena no cumple los criterios mínimos, llamar a la función nuevamente
        return call_user_func("generateRandomString",($stringLength) );
    }
}//fin de la función generateRandomString

//llamar a la función para generar una cadena aleatoria con mayúsculas, minúsculas, números y caracteres especiales
//pasamos la longitud de la cadena requerida, en este ejemplo generará una cadena de 32 caracteres
$code = generateRandomString(32);

echo $code;
12 abr 2020 16:43:40
Comentarios

¡Excelente respuesta! Sería incluso un poco mejor si le das formato al código. Además, el símbolo $ debe escaparse. También, corrígeme si me equivoco, pero no hay razón para usar call_user_func en lugar de llamar a la función directamente.

Slbox Slbox
15 abr 2020 03:25:54

La razón por la que uso call_user_function es que la función crea una cadena aleatoria de cualquier longitud que especifiques. En caso de que no se cumplan los criterios de complejidad de la cadena aleatoria, la función simplemente se ejecutará nuevamente automáticamente, por ejemplo, si la función generó una cadena aleatoria que no contenía una letra mayúscula o un número, entonces call_user_function ejecuta la función nuevamente. La función solo devolverá una cadena aleatoria una vez que cumpla con todos los criterios de complejidad.

User User
15 abr 2020 20:40:21

He probado usando base_64_encode en el $code generado por la función de cadena aleatoria adjunta a $url y luego recuperarlo con el método GET sin problemas.

User User
15 abr 2020 20:48:00

También es una muy mala idea simplemente enviar el $code directamente a get_user_meta ya que esto es igual que almacenar contraseñas en texto plano en una base de datos. Deberías encriptar el $code almacenado en get_user_meta. Luego desencriptar usando el $code adjunto a la $url. De lo contrario, simplemente estás evitando todo el punto del hashing de seguridad de WordPress. Mira tus wp-users, no hay contraseñas almacenadas en texto plano, por lo que también necesitas hashear el $code de restablecimiento de contraseña antes de almacenarlo. Usa la función password_hash() para encriptar y password_verify() para desencriptar.

User User
15 abr 2020 21:00:31

No creo que un código de activación de usuario necesariamente necesite ser hasheado para ser seguro. Si alguien accede a tu base de datos, los códigos de activación de usuario realmente no importan, pueden simplemente establecer el email como confirmado, aunque probablemente tengan cosas mucho peores en mente. Respecto a call_user_function, todavía no entiendo por qué no puedes simplemente llamar a la función desde sí misma. ¿Una función no puede llamarse a sí misma en PHP?

Slbox Slbox
15 abr 2020 22:16:50

Sí, la función se llama directamente en el código anterior donde dice "$code = generateRandomString(32);". La función está usando preg match para verificar si la cadena aleatoria generada tiene Letras Mayúsculas/Letras Minúsculas/Números/Caracteres Especiales. Si la cadena aleatoria no cumple con los criterios de complejidad, call_user_function ejecuta automáticamente la función nuevamente. La función seguirá generando cadenas aleatorias hasta que una de ellas cumpla con los criterios de complejidad especificados en el preg match. La primera cadena aleatoria podría ser "abcdefghijklmnopqrstuvwxyzabcdef"

User User
15 abr 2020 23:52:02

"abcdefghijklmnopqrstuvwxyzabcdef" no cumple con los requisitos de complejidad ya que no contiene números, letras mayúsculas o caracteres especiales, por lo que call_user_function vuelve a llamar automáticamente a la función hasta que obtenemos una cadena aleatoria que cumpla con los requisitos de complejidad. Está actuando como un bucle.

User User
15 abr 2020 23:53:27

Me pregunto por qué, si WordPress está cifrando la contraseña de la cuenta en la base de datos, no se cifraría también la clave de restablecimiento para esa misma contraseña.

User User
15 abr 2020 23:55:17

¿Crees que WordPress no debería cifrar las contraseñas de las cuentas en la base de datos?

User User
15 abr 2020 23:56:29

@MikeMoy Acabo de encontrar tu respuesta y me gustaría mucho usar tu código si está bien. ¿Lo usaría junto con las primeras 2 acciones de la primera respuesta, o estaría reemplazando parte del código de la primera respuesta?

Joe Bloggs Joe Bloggs
7 may 2021 11:00:39
Mostrar los 5 comentarios restantes