Cómo validar de verdad el login de Telegram en PHP: HMAC y caducidad
Tomar el hash del widget de login de Telegram tal cual llega abre la puerta a la suplantación de identidad. La verificación correcta deriva la clave del token del bot y compara con hash_equals.

El widget de login de Telegram resuelve la parte cómoda del asunto: el usuario se autentica con su cuenta y la aplicación recibe un puñado de parámetros en el callback. La parte que suele hacerse mal es la siguiente: esos parámetros llegan por el cliente y, si el backend los acepta sin verificar la firma, cualquiera puede fabricar un id y hacerse pasar por otra persona.
El método correcto es el que Telegram describe para validar el payload del widget. Se ordenan alfabéticamente todos los parámetros recibidos —menos hash—, se unen en pares clave=valor separados por salto de línea, se calcula la clave secreta como el SHA-256 del token del bot en bruto y se comprueba la firma HMAC-SHA-256 con esa clave. Si el hash calculado coincide con el que llegó, la petición es auténtica. La comparación nunca se hace con doble igual, sino con hash_equals, que tarda lo mismo independientemente de dónde fallen los bytes y evita los ataques de temporización.
El detalle que se salta media implementación
La firma por sí sola no basta. El payload incluye auth_date, y un atacante que consiga reutilizar una autenticación antigua entra igual. Por eso conviene rechazar cualquier auth_date más viejo que un margen definido: el ejemplo de referencia trabaja con 24 horas por defecto y con 12 en el controlador, pasando el límite como parámetro al validador. Sin ese filtro, la verificación criptográfica queda coja.
Un validador de este tipo en PHP se aísla en su propia clase, recibe el token del bot en el constructor, comprueba que no venga vacío y devuelve un booleano. También verifica que hash y auth_date estén presentes antes de tocar nada, porque una petición sin ellos no es verificable y no debe pasar de ahí.
En el lado de Yii2, la integración pasa por añadir una columna telegram_id a la tabla de usuarios, con índice único, mediante una migración que además sabe deshacerse en el safeDown. La acción del controlador recupera los parámetros del request, saca el token del bot de las variables de entorno o de los parámetros de la aplicación —nunca del repositorio— y lanza un error de servidor si falta. Tras validar, busca un usuario con ese telegram_id: si existe, inicia sesión; si no existe pero hay alguien ya autenticado, enlaza la cuenta en lugar de crear una nueva; y si no hay sesión, registra un usuario nuevo. El guardado de la vinculación se hace con validación y limitado al campo telegram_id.
Qué queda fuera
El artículo de referencia no cubre el pintado del widget en HTML ni la configuración del almacenamiento de sesión, que cada proyecto resuelve a su manera. Tampoco entra en cómo se obtiene el token del bot ni en su rotación. Lo que sí queda claro es que la verificación pertenece al backend: el widget es una comodidad de interfaz, no una fuente fiable.
Para quien mantenga autenticación federada en PHP, la lección se generaliza. Cualquier flujo que acepte identificadores desde el cliente necesita una firma que el servidor pueda recalcular con un secreto que nunca viaja. Telegram usa HMAC-SHA-256 sobre el token del bot; otros proveedores hacen variantes del mismo patrón, y el error de creerse el payload es idéntico en todos.

