¿Cómo configurar la verificación de correo electrónico del usuario después del registro?
¿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

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' ) );
}
}
}

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;

¡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.

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.

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.

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.

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?

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"

"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.

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.

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