0

When a user accesses the website through the login, a page profile.php is displayed which shows the user's data (the password is not shown) in a table and displays a form to modify this data. The problem is that all the data is modified correctly except the password, which is modified but it is not the one sent by the form.

Connection to the database:

<?php

include('../php/.env.php');

class DB {

    public function __construct()
    {
        if ($_SESSION['usuario'][2] != 'admin' || $_SESSION['usuario'][2] != 'user') {
            header('location:../php/login.php');
            exit();
        }
    }

    public static function conn()
    {
        // conexión a la base de datos
        try {
            $conn = new PDO("mysql:host=" . SERVIDOR . ";dbname=" . BD, USUARIO, PASSWORD);
            $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $conn;
        } catch (PDOException $e) {
            echo 'HA FALLADO LA CONEXIÓN: ' . $e->getMessage();
        }
    }
}

perfi.php (form)

<?php
            <div class="container p-4">
                <div class="row">
                    <div class="col-md-5 mt-4">
                        <div class="border">
                            <div class="card-body">
                                <!-- form no tiene el atributo action porque lo vamos a procesar por JS -->
                                <form class="form" id="form-editar">
                                    <input type="hidden" id="idUser"/>
                                    <div class="form-label">
                                        <input type="text" id="nombre" placeholder="Nombre" class="form-control mb-3" />
                                        <input type="text" id="apellidos" class="form-control mb-3" placeholder="Apellidos">
                                        <input type="tel" id="telefono" class="form-control mb-3" placeholder="Teléfono">
                                        <input type="text" id="direccion" class="form-control mb-3" placeholder="Dirección">
                                        <input type="password" id="contrasena" class="form-control mb-3" autocomplete="off" placeholder="Contraseña">
                                    </div>
                                    <button type="submit" class="btn btn-primary col-12 text-center">
                                        Actualizar perfil
                                    </button>
                                </form>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-7 text-center mt-4">
                        <table class="table table-bordered table-sm">
                            <thead>
                                <tr>
                                    <td>Id</td>
                                    <td>Nombre</td>
                                    <td>Apellidos</td>
                                    <td>Teléfono</td>
                                    <td>Dirección</td>
                                    <td>Modificar perfil</td>
                                </tr>
                            </thead>
                            <tbody id="datos"></tbody>
                        </table>
                    </div>
                </div>
            </div>

MYSQL

users_data contains: idUser(PK), first name, last name, phone number, address

users_login contains: idLogin, idUser(FK), usuario, contrasena, rol

datos.php (shows the user's data )

<?php
// realiza una consulta a la bbdd de los datos del usuario y las envía al navegador

include('./DB.php');

session_start();

if ($_SESSION['usuario'][2] == 'user' || $_SESSION['usuario'][2] == 'admin') {


    $conexion = DB::conn();
    $sentencia = "SELECT * FROM users_data WHERE idUser = :idUser";
    $consulta = $conexion->prepare($sentencia);
    $consulta->execute(array(':idUser' => $_SESSION['usuario'][0]));

    $json = [];
    while ($row = $consulta->fetch(PDO::FETCH_ASSOC)) {
        $json[] = [
            'idUser' => $row['idUser'],
            'nombre' => $row['nombre'],
            'apellidos' => $row['apellidos'],
            'telefono' => $row['telefono'],
            'direccion' => $row['direccion']
        ];
    }
    $consulta->closeCursor();
    $conexion = null;
    $jsonstring = json_encode($json);
    echo $jsonstring;
} else {
    header('location:../php/login.php');
}

prueba.php (it shows the user's data in the form for its later modification)

<?php

include('./DB.php');

session_start();

    $conexion = DB::conn();
    $sentencia = "SELECT * FROM users_data WHERE idUser = :idUser";
    $consulta = $conexion->prepare($sentencia);
    $consulta->execute(array(':idUser' => $_POST['idUser']));

    $json = [];
    while ($row = $consulta->fetch(PDO::FETCH_ASSOC)) {
        $json[] = [
            'idUser' => $row['idUser'],
            'nombre' => $row['nombre'],
            'apellidos' => $row['apellidos'],
            'telefono' => $row['telefono'],
            'direccion' => $row['direccion']
        ];
    }

    $consulta->closeCursor();
    $conexion = null;
    $jsonstring = json_encode($json[0]);
    echo $jsonstring;

modificarDatos.php (modifies the user data in the table users_data and users_login.)

include('./DB.php');

session_start();

if (isset($_POST['idUser'])) {
    $idUser = filter_input(INPUT_POST, 'idUser', FILTER_SANITIZE_NUMBER_INT, FILTER_VALIDATE_INT);
    $nombre = htmlentities($_POST['nombre']);
    $apellidos = htmlentities($_POST['apellidos']);
    $telefono = filter_input(INPUT_POST, 'telefono', FILTER_SANITIZE_NUMBER_INT, FILTER_VALIDATE_INT);
    $direccion = htmlentities($_POST['direccion']);
    $pass = htmlentities($_POST['contrasena']);
    $contrasena = password_hash($pass, PASSWORD_BCRYPT);


    $conexion = DB::conn();
    $sentencia = "UPDATE users_data SET nombre = :nuevoNombre, apellidos = :nuevoApellido, telefono = :nuevoTelefono, direccion = :nuevaDireccion  WHERE idUser = $idUser";
    $consulta = $conexion->prepare($sentencia);
    $consulta->bindParam(":nuevoNombre", $nombre);
    $consulta->bindParam("nuevoApellido", $apellidos);
    $consulta->bindParam(":nuevoTelefono", $telefono);
    $consulta->bindParam(":nuevaDireccion", $direccion);
    $consulta->execute();
    $sentencia2 = "UPDATE users_login SET contrasena = :nuevaContrasena WHERE idLogin = $idUser";
    $consulta = $conexion->prepare($sentencia2);
    $consulta->bindParam(":nuevaContrasena", $contrasena);
    $consulta->execute();

}

JS - AJAX

$(document).ready(function () {
  console.log("jQuery esta funcionando");

  // obtains user data
  function mostrarDatos() {
    $.ajax({
      type: "GET",
      url: "datos.php", 
      success: (response) => {
        let datos = JSON.parse(response);
        let template = "";

        datos.forEach((dato) => {
          // scrolls through and displays user data
          template += `
            <tr idUser="${dato.idUser}"class="align-middle">
              <td>${dato.idUser}</td>
              <td>${dato.nombre}</td>
              <td>${dato.apellidos}</td>
              <td>${dato.telefono}</td>
              <td>${dato.direccion}</td>
              <td>
                <button class="edit-datos btn btn-danger">
                  Modificar datos
                </button>
              </td>
            </tr>
          `;
        });
        $("#datos").html(template);
      },
    });
  }
  mostrarDatos();

  // displays the user data in the form so that they can be modified
  $(document).on("click", ".edit-datos", () => {
    let element = $(this)[0].activeElement.parentElement.parentElement;
    let idUser = $(element).attr("idUser");
    $.post("prueba.php", { idUser }, (response) => {
      let datos = JSON.parse(response);
      $("#idUser").val(datos.idUser);
      $("#nombre").val(datos.nombre);
      $("#apellidos").val(datos.apellidos);
      $("#telefono").val(datos.telefono);
      $("#direccion").val(datos.direccion);
      $("#contrasena").val(datos.contrasena);
    });
  });

  $("#form-editar").submit((e) => {
    e.preventDefault();
    const postData = {
      idUser: $("#idUser").val(),
      nombre: $("#nombre").val(),
      apellidos: $("#apellidos").val(),
      telefono: $("#telefono").val(),
      direccion: $("#direccion").val(),
      contrasena: $("#contrasena").val()
    };
    $.post("modificarDatos.php", postData, (response) => {
      mostrarDatos(response);
      $("#form-editar").trigger("reset"); // resetea el formulario
    });
  });
});
Miguel
  • 9
  • 5
  • 2
    `"UPDATE users_login SET contrasena = :nuevaContrasena WHERE idLogin = $idUser";` You are vulnerable to SQL injection, use parameter binding all all the parameters here. – Alex Howansky Dec 21 '22 at 16:04
  • 1
    _"which is modified but it is not the one sent by the form"_ What does this mean? You're hashing the provided password with `password_hash()` (as you should), so the value that gets stored in the database isn't going to be the plain text password that the user types. – Alex Howansky Dec 21 '22 at 16:07
  • @AlexHowansky I don't understand what you mean by 'use parameter binding all all the parameters here' – Miguel Dec 21 '22 at 16:08
  • 1
    Also note, you are not checking that the user is logged in before allowing them to change a password. So, any user can change any other user's password by hitting that endpoint directly. You need to prompt for the current password, and check that it's correct, before making the changes. – Alex Howansky Dec 21 '22 at 16:08
  • 1
    You should be binding a value for idUser: `"UPDATE users_login SET contrasena = :nuevaContrasena WHERE idLogin = :idUser";` like you do in prueba.php. – Alex Howansky Dec 21 '22 at 16:09
  • `don't understand what you mean by 'use parameter binding all all the parameters here'`...simple: Why didn't you make $idUser into a parameter, instead of adding it directly to the query? You obviously know how to use parameters, so why did you not do it on this one? You must parameterise _all_ external inputs into the query, to make it safe and reliable. – ADyson Dec 21 '22 at 16:14
  • P.S. Your usage of `htmlentities()` is inappropriate and potentially problematic. `htmlentities()` is an _output_ filter, only to be used _specifically_ when _outputting_ data into a HTML document. It is designed only to help protect against XSS. It should not be used at any other time, such as when receiving input data -in the worst case it can change or corrupt your data unnecessarily in that situation. It also has nothing to do with preventing SQL injection. – ADyson Dec 21 '22 at 16:15
  • See [when to use htmlspecialchars() function?](https://stackoverflow.com/questions/4882307/when-to-use-htmlspecialchars-function) (htmlspecialchars and htmlentities are very similar, and the same advice applies to both of them). You should be using this only when echoing user-generated content back into your HTML pages, not at any other time. – ADyson Dec 21 '22 at 16:15
  • You're using `filter_input()` which is a step in the right direction, but it's not always sufficient. E.g., what happens in your code if the user provides something that's not an integer and that function returns null? You'll get a query with a syntax error and the page will 500. – Alex Howansky Dec 21 '22 at 16:17
  • @AlexHowansky if I check that the user is logged in, just shorten the code here – Miguel Dec 21 '22 at 17:40
  • @ADyson so should I just put $name = $_POST['name'] ? or should I use a filter_input() ? thought it was needed to validate – Miguel Dec 21 '22 at 17:51
  • @ADyson should i write this? "UPDATE users_login SET contrasena = :newContrasena WHERE idLogin = :idUser"; $sentencia->bindParam(":idUser", $idUser) – Miguel Dec 21 '22 at 17:58
  • exactly, yes, that's right. – ADyson Dec 21 '22 at 18:09
  • `should I use a filter_input()`...if it's useful. htmlspecialchars/htmlentities doesn't do validation, as I explained. filter_input can be useful for specific things - there's a list in the documentation of the validations it can do. https://www.php.net/manual/en/filter.filters.validate.php – ADyson Dec 21 '22 at 18:09
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community Dec 21 '22 at 19:46

0 Answers0