Corregir la validación de los enlaces para restablecer contraseñas

CiberseguridadBeginner
Practicar Ahora

Introducción

El portal de miembros de Harbor permite recuperar una cuenta cuando se olvida la contraseña. El correo de restablecimiento lleva un token temporal: poseerlo debería autorizar un solo cambio de contraseña para una sola cuenta. Soporte ha descubierto que los enlaces se reutilizan y pueden cambiar la contraseña de otra cuenta.

Primero recuperarás una cuenta ficticia normalmente, observarás el comportamiento inseguro y repararás un pequeño controlador de Python. No necesitas experiencia en Python: la lección explica funciones y condiciones antes de editar. Debes saber introducir un comando y cambiar de pestaña.

Usa la pestaña Portal Web para la aplicación, Mailbox Web para el correo simulado y App Server para el código preparado. Todo funciona en esta instancia de formación independiente. No se envía correo externo. Los tokens, las comparaciones de caducidad, las transacciones, los cambios de contraseña y el inicio de sesión son reales; esta aplicación no es un sistema de identidad de producción.

Recuperar una cuenta normalmente

Selecciona la pestaña Portal Web en la interfaz del laboratorio para comenzar este paso.

En este paso, seguirás un correo de restablecimiento e iniciarás sesión con la nueva contraseña para establecer el funcionamiento que debe conservar la reparación.

Una cuenta identifica a una persona; una contraseña demuestra que puede iniciar sesión. Si se olvida, el enlace autoriza temporalmente su cambio. El token del enlace no es la contraseña. Este buzón local permite actuar como propietario de las cuentas ficticias maya y noah, ambas con StartPass42! inicialmente. Usa solo estas cuentas de formación.

Formularios de Portal Web para solicitar un enlace, cambiar la contraseña e iniciar sesión

Cambia a la pestaña Portal Web y busca Request a reset link. Introduce maya en Recovery account y pulsa Send reset link. Aparece Link requested. Open Local Mailbox. Cambia a la pestaña Mailbox Web. Si ves un formulario o resultado de restablecimiento, selecciona Local Mailbox para volver a la lista de mensajes. Actualiza la lista para ver el mensaje más reciente arriba. Muestra el destinatario, Open reset link y una cadena de token.

Mailbox Web muestra el nuevo mensaje con el enlace de restablecimiento

Usa el enlace y el token que muestra tu pestaña Mailbox Web actual; los valores de los tokens varían entre entornos.

Pulsa Open reset link en ese mensaje. El formulario se abre en la misma pestaña con Token y Reset account rellenados. No los cambies; introduce FirstPass42! en New password y pulsa Reset password. Espera Reset accepted y HTTP 200.

Resultado de un restablecimiento correcto dentro de la pestaña Mailbox Web

HTTP es la conversación de peticiones y respuestas entre navegador y servidor. Aquí 200 significa aceptado, 403 indica una petición denegada y 500 un error de aplicación. Un error de conexión es distinto de una denegación de seguridad.

Cambia a la pestaña Portal Web. Si ves el resultado de la solicitud, selecciona Back to portal para volver a los formularios. Usa Sign in: Login account maya, Password FirstPass42!, botón Sign in. Espera Login accepted. Un restablecimiento funcional cambia la contraseña real, no solo un texto de la página.

Si pierdes el enlace, solicita otro con el mismo formulario y usa el mensaje más reciente. Dura 30 minutos. Una errata o un enlace caducado no requieren reiniciar el entorno.

Observar el límite roto

Selecciona la pestaña Portal Web en la interfaz del laboratorio para comenzar este paso.

En este paso, harás cambios de demostración desechables para distinguir propietario, vigencia y permiso de un solo uso del token.

El navegador envía campos con nombre y valor. Como el usuario puede cambiarlos, el servidor no puede confiar en Reset account solo porque el correo lo rellenó. El servidor guarda un registro separado:

Campo Significado
account Cuenta autorizada a usar el token
expires_at Último momento permitido, en segundos desde un origen de reloj fijo
used 0 antes del uso; 1 después

Un token nuevo solo sirve para su propietario antes de caducar. Un cambio correcto lo deja usado y nunca debe volver a ser utilizable. Uno sin usar también puede caducar con el tiempo. Ninguno de esos estados autoriza otro cambio.

Reutiliza un enlace

Cambia a la pestaña Portal Web, selecciona Back to portal si ves un resultado y solicita un enlace nuevo para maya. Cambia a la pestaña Mailbox Web. Si ves un formulario o resultado de restablecimiento, selecciona Local Mailbox para volver a la lista de mensajes. Actualiza la lista, abre el enlace más reciente y cambia la contraseña de maya a DemoPass42!. En la pestaña Mailbox Web, selecciona Local Mailbox en el resultado para volver a la lista. Reabre el enlace del mismo mensaje y cambia la contraseña de maya a ReplayDemo42!. Ambas solicitudes muestran Reset accepted. La segunda debería denegarse: es una repetición, reutilizar un permiso que ya debía agotarse.

Prueba otra cuenta

En la pestaña Mailbox Web, selecciona Local Mailbox y reabre otra vez el enlace del mismo mensaje. Cambia Reset account a noah y New password a CrossDemo42!; pulsa Reset password. Se acepta. Cambia a la pestaña Portal Web y selecciona Back to portal para volver al formulario de inicio de sesión. Entra como noah con CrossDemo42!. Un enlace de maya ha cambiado la contraseña de noah: el servidor no vinculó el token con su cuenta.

Prueba un token caducado

Permanece en la pestaña Portal Web y selecciona Back to portal para volver al formulario de restablecimiento. Introduce expired-demo en Token, maya en Reset account y ExpiredDemo42! en New password. Este token preparado caducó una hora antes de la preparación. Pulsa Reset password. También se acepta: el servidor ignoró el reloj. No necesitas esperar un temporizador para observar el fallo.

Son cuentas desechables; las pruebas siguientes usan copias limpias. Conserva el estado observado, sin editar directamente la base de datos.

Vincular y consumir el token

Selecciona la pestaña App Server en la interfaz del laboratorio para comenzar este paso.

En este paso, repararás la pequeña función del servidor y la probarás sin modificar las cuentas de demostración activas.

Cambia a la pestaña App Server, que empieza en /home/labex/project/reset. cat muestra un archivo de texto; su argumento es el nombre. Lee el controlador:

Cambia a la pestaña App Server antes de ejecutar el siguiente comando.

cat handler.py

La primera línea importa dos funciones auxiliares. def define una función; los nombres entre paréntesis son parámetros, las entradas de una llamada. eligible(row, account, now) recibe el registro del token, la cuenta enviada y la hora del servidor. row["account"] lee un campo. row is None significa que no se encontró token. El bloque sangrado de if solo se ejecuta si se cumple la condición; return False deniega y return True concede permiso. Cuatro espacios iniciales definen aquí un bloque de Python.

La función actual devuelve True para cualquier token existente, lo que explica los tres fallos. Además, reset llama a unsafe_reset, que cambia la contraseña sin consumir el token.

La reparación exige todas las condiciones. == compara valores, > significa mayor que y and exige ambos lados verdaderos. or aceptaría cualquiera y una propiedad válida ocultaría otra inválida. Una caducidad futura es mayor que now; la igualdad ya significa caducado. used == 0 solo permite un token sin usar.

La función preparada consume_and_reset inicia una transacción SQLite, lee el token actual, llama a tu eligible y confirma juntos el cambio de contraseña y used = 1. Esto es atomicidad: ambos cambios tienen éxito juntos. Bloquea antes de leer para impedir que dos solicitudes concurrentes vean el token sin usar. No implementas bloqueo ni hash de contraseñas.

Abre el archivo con nano, un editor de texto del terminal:

nano handler.py

Sustituye el contenido por este código. Usa flechas y Retroceso/Supr para borrar lo anterior y pega la sustitución completa. Conserva espacios y signos; los paréntesis permiten dividir la condición en varias líneas.

from framework import unsafe_reset, consume_and_reset


def eligible(row, account, now):
    if row is None:
        return False
    return (row["account"] == account
            and row["expires_at"] > now
            and row["used"] == 0)


def reset(db, token, account, password):
    return consume_and_reset(db, token, account, password, eligible)

Guarda con Ctrl+O, pulsa Enter para confirmar handler.py y sal con Ctrl+X. Cada solicitud Web lee el archivo guardado; no hace falta reiniciar. Si aparece HTTP 500, abre otra vez y compara sangría, corchetes y comillas.

python3 ejecuta un programa Python. El comprobador recibe la ruta del controlador y arranca una aplicación HTTP separada con datos SQLite temporales. Prueba el código real sin repararlo ni cambiar las cuentas activas:

python3 /opt/reset-lab/check.py handler.py

Espera once líneas PASS, incluidas valid reset accepted, replay denied y competing use has exactly one winner. FAIL nombra la propiedad incorrecta. Denegar todo falla las pruebas de recuperación y acceso legítimos. Cambiar campos del navegador o la base activa no repara el código probado con datos nuevos. Reabre nano handler.py, corrige, guarda y repite el comprobador cuando sea necesario.

Demostrar que la recuperación sigue funcionando

Selecciona la pestaña Portal Web en la interfaz del laboratorio para comenzar este paso.

En este paso, usarás el servicio reparado y relacionarás sus resultados con las pruebas de seguridad aisladas.

Usa un enlace nuevo una sola vez

Cambia a la pestaña Portal Web, selecciona Back to portal si ves un resultado y solicita un enlace nuevo para maya. Cambia a la pestaña Mailbox Web. Si ves un formulario o resultado, selecciona Local Mailbox para volver a la lista. Actualiza la lista, abre el enlace más reciente y cambia la contraseña de maya a ResetSafe42!. Espera Reset accepted y HTTP 200. En la pestaña Mailbox Web, selecciona Local Mailbox para volver a la lista; reabre el enlace del mismo mensaje e intenta ReplayAgain42! para maya. Espera Reset denied y HTTP 403.

Se rechaza la reutilización del enlace tras reparar el controlador

Conserva el enlace sin usar del propietario

Cambia a la pestaña Portal Web, usa Back to portal si ves un resultado y solicita otro enlace nuevo de maya. Cambia a la pestaña Mailbox Web, selecciona Local Mailbox, actualiza y abre el enlace más reciente. Cambia Reset account a noah e intenta BlockedCross42!. Espera Reset denied. Esa denegación no debe consumir el enlace sin usar del propietario: selecciona Local Mailbox en la pestaña Mailbox Web, reabre el mismo mensaje, conserva maya como Reset account y establece ResetSafe42!. Debe funcionar.

Rechaza la caducidad y conserva el inicio de sesión

En la pestaña Mailbox Web, selecciona Back to portal para abrir el formulario de restablecimiento. Prueba Token expired-demo, Reset account maya y New password BlockedExpired42!. Espera Reset denied. Cambia a la pestaña Portal Web y selecciona Back to portal para volver al formulario de inicio de sesión. Entra como maya con ResetSafe42!. Debe mostrar Login accepted: los intentos fallidos no cambiaron la contraseña. Para probar también noah, selecciona Back to portal otra vez y entra con su contraseña de demostración CrossDemo42!; el controlador reparado conserva las cuentas ajenas existentes.

En la pestaña App Server, repite las pruebas de regresión desechables:

Cambia a la pestaña App Server antes de ejecutar el siguiente comando.

python3 /opt/reset-lab/check.py handler.py

Las once deben pasar, incluidas dos solicitudes simultáneas con un único cambio aceptado. Repetirlas no altera registros activos. Se combinan recuperación permitida y abuso denegado; desactivar la página o denegar siempre no es una reparación correcta.

La aplicación usa entrega simulada e identidad simplificada. En producción también hacen falta correo seguro, límites de frecuencia, gestión de sesiones y política de contraseñas, fuera de este ejercicio. La guía de OWASP amplía ese contexto.

Resumen

Seguiste un restablecimiento real, demostraste repetición, cruce de cuentas y caducidad, y reparaste un pequeño controlador Python. Propietario almacenado, caducidad futura, estado sin usar y consumo atómico actúan juntos. Las pruebas HTTP desechables demuestran recuperación válida, abuso denegado, cuentas ajenas intactas y un único ganador concurrente. El desafío del portal de personal aplica esas reglas a otra condición incompleta.