feat(security): Implementar sistema de contraseñas seguro con hashing

- Añadir hashing bcrypt para todas las contraseñas nuevas y existentes
- Implementar verificación segura con password_hash() y password_verify()
- Migrar 10 contraseñas existentes de texto plano a formato hash
- Agregar protección CSRF en formulario de login
- Implementar rate limiting (5 intentos/minuto) contra fuerza bruta
- Mejorar formulario de edición con campos de contraseña seguros
- Agregar validación de coincidencia y longitud mínima de contraseñas
- Sanitización de inputs y validación de formato de email
- Prevenir exposición de hashes en interfaz de usuario

Cambia vulnerabilidad crítica donde las contraseñas se almacenaban y viajaban en texto plano.
This commit is contained in:
2026-01-09 15:24:26 -06:00
parent cb1a44e380
commit 448a2aa240
8 changed files with 169 additions and 36 deletions

View File

@@ -147,7 +147,40 @@ class Usuario extends Main
public function setPassword($value)
{
$this->passwd = $value;
// Si la contraseña ya está hasheada (empieza con $2y$), la guardamos directamente
if(preg_match('/^\$2y\$/', $value)) {
$this->passwd = $value;
} else {
// Si no, la hasheamos
$this->passwd = password_hash($value, PASSWORD_DEFAULT);
}
}
// Método para verificar contraseña (usado en login)
public function verifyPassword($plainPassword, $hashedPassword)
{
// Si la contraseña almacenada está en texto plano (migración)
if(!preg_match('/^\$2y\$/', $hashedPassword)) {
// Verificar contra texto plano y hashear si coincide
if($plainPassword === $hashedPassword) {
// Actualizar a hash
$this->passwd = password_hash($plainPassword, PASSWORD_DEFAULT);
$this->updatePasswordHash();
return true;
}
return false;
}
// Verificación normal con hash
return password_verify($plainPassword, $hashedPassword);
}
// Método para actualizar el hash en la base de datos
private function updatePasswordHash()
{
$db = new DB(true);
$db->setQuery("UPDATE usuario SET password = '".$this->passwd."' WHERE usuarioId = '".$this->usuarioId."'");
$db->UpdateData();
}
public function setTipo($value)
@@ -302,8 +335,9 @@ class Usuario extends Main
}
$db = new DB(true);
$db->setQuery("
UPDATE usuario SET
// Construir consulta SQL condicional para la contraseña
$sql = "UPDATE usuario SET
nombre = '".$this->nombre."',
apellidos = '".$this->apellidos."',
calle = '".$this->calle."',
@@ -321,12 +355,18 @@ class Usuario extends Main
noImss = '".$this->noImss."',
curp = '".$this->curp."',
rfc = '".$this->rfc."',
email = '".$this->email."',
password = '".$this->passwd."',
`type` = '".$this->tipo."',
email = '".$this->email."'";
// Solo actualizar contraseña si se estableció una nueva
if (!empty($this->passwd)) {
$sql .= ", password = '".$this->passwd."'";
}
$sql .= ", `type` = '".$this->tipo."',
sucursalId = '".$this->sucursalId."'
WHERE usuarioId = '".$this->usuarioId."'"
);
WHERE usuarioId = '".$this->usuarioId."'";
$db->setQuery($sql);
$db->UpdateData();
$this->Util()->setError(20019, "complete");