Creando un área de cliente para portfolios con PHP y MySQL – Segunda parte

Repasaremos algunos problemas de seguridad para nuestro formulario de registro. Cuando se trata de bases de datos, es importante asegurarse de que la información del cliente es segura. También repasaremos algunas maneras de asegurarnos de que el usuario introduce los datos correctos. Al final de este manual nos quedaremos con una base de datos de estructura sólida, un formulario de registro que introduzca con éxito los datos correctos y un plan general para el proyecto.

Vamos a repasar la seguridad cuando se trabaja con PHP y bases de datos, y aplicaremos algunas medidas de seguridad especiales para nuestro formulario de registro.

Repasaremos inyecciones SQL, seguridad básica de los formularios HTML, cifrado de contraseñas MD5, y más.

Seguridad del formulario

Esta parte será un poco un minitutorial HTML muy básico, pero debe ser cubierto porque muchas personas podéis estar trabajando con formularios seguros por primera vez. El primer paso para asegurar nuestro formulario de registro es ocultar la contraseña en el formulario HTML.

Hacer esto ayudará a asegurar ante cualquier persona que esté delante de la pantalla el robo de contraseñas. Es un paso obvio, pero esencial.

Para ocultar la contraseña en el formulario, simplemente usamos el atributo “type” para el campo de contraseña a “password” en lugar de “text”.

 <form action="singup.php" method="post">
 Nombre de usuario:<br />
 <input name="username" type="text" autofocus /><br />
 Contraseña:<br />
 <input id="pw" name="password" type="password" /><br />
 Repetir contraseña:<br />
 <input id="pw2" name="password" type="password" /><br />
 Email:<br />
 <input name="email" type="email" /><br />
 Dirección de Paypal:<br />
 <input name="paypal" type="email" /><br />
 
 <input type="submit" value="Registrarse" />
 </form>

Cifrar la contraseña

La segunda forma de seguridad es cifrar la contraseña. Si vemos a nuestros clientes de prueba en la base de datos, comprobaremos que el campo de contraseña está completamente abierta para los administradores u otros espectadores de la base de datos. Es, por supuesto, una mala práctica dejarla así a la vista.

Para resolver nuestro problema, vamos a cifrar la contraseña con la función md5() en PHP. Esto nos permitirá cifrar una contraseña que ni siquiera un administrador podría descifrar. Con bastante facilidad podremos colocar nuestra variable password como parámetro para la función md5(); md5($password).

<?php
// Necesitaremos conectarnos a la base de datos
require('db.php');
// Si aún no se han introducido datos, se ignora el código PHP
// Y se carga el resto de la página.
if (!empty($_POST)) { 

// Guardamos los valores de los campos del formulario en variables.
$username = $_POST["username"];
$password = $_POST["password"];
$pw = md5($password);
$email = $_POST["email"];
$paypal = $_POST["paypal"]; 

// Creamos la consulta que nos insertará los datos
$result = "INSERT INTO clients (client_ID, username, password, email, paypal) VALUES ('', '$username', '$pw', '$email', '$paypal')"; 
// Ejecutamos la consulta
$sql = mysql_query($result);
// Mostramos un mensaje de confirmación 
echo "Gracias por inscribirte.<br />"; 
echo "Ahora puedes <a href='login.php'>iniciar sesión</a>";
}
?>

Lo que sucede aquí es, en primer lugar, que la contraseña se toma del método POST a través de nuestro formulario, y es puesta en la variable $password. Entonces nosotros crearemos otra variable “$pw” y pondremos la cadena $password cifrada en ella. La longitud de nuestra contraseña cifrada SIEMPRE será de 32 caracteres, independientemente de la longitud de contraseña que hayamos elegido nosotros.

Como te darás cuenta más adelante, en nuestra consulta para insertar la información en la base de datos hemos insertado la versión cifrada de la contraseña “$pw”.

Nuestra contraseña está ahora codificada y no puede ser leída como texto sin formato.

Inyección SQL

La inyección SQL es un tipo de técnica maliciosa que se puede utilizar para manipular una base de datos no segura mediante la introducción de datos bien pensados en un formulario.

El ejemplo a continuación es una declaración básica de SQL que selecciona datos de “users” donde el “name” es igual a lo que fue la entrada en el campo de formulario llamado “formfield”.

SELECT * FROM users WHERE name = '$_POST(formfield)';

Si alguien fuese a probar con un nombre, “Jose Carlos”, por ejemplo, el PHP resultante a ejecutar sería el siguiente:

SELECT * FROM users WHERE name = 'Jose Carlos';

Seleccioné el nombre de “Jose Carlos” en la tabla “users”. Sin embargo, una técnica maliciosa podría ser algo como esto:

SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '%';

Esto parece ser un montón de tonterías hasta que nos ponemos en nuestro código SQL:

SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM DATA WHERE name LIKE '%';

Esto crea un efecto completamente diferente para el código, que encuentra un nombre “a”, elimina la tabla “users”, y selecciona todos los datos de “DATA” (%). Cualquiera puede ver que esto podría llegar a ser muy destructivo.

Afortunadamente, la solución es muy fácil. Sólo con añadir mysql_real_escape_string() alrededor de cada una de nuestras variables asociadas con un campo de entrada del formulario. Por ejemplo, debajo tenemos el formulario de registro actualizado.

<?php
// Necesitaremos conectarnos a la base de datos
require('db.php');
// Si aún no se han introducido datos, se ignora el código PHP
// Y se carga el resto de la página.
if (!empty($_POST)) { 

// Guardamos los valores de los campos del formulario en variables.
$username = mysql_real_escape_string($_POST["username"]);
$password = mysql_real_escape_string($_POST["password"]);
$pw = md5($password);
$email = mysql_real_escape_string($_POST["email"]);
$paypal = mysql_real_escape_string($_POST["paypal"]); 

// Creamos la consulta que nos insertará los datos
$result = "INSERT INTO clients (client_ID, username, password, email, paypal) VALUES ('', '$username', '$pw', '$email', '$paypal')"; 
// Ejecutamos la consulta
$sql = mysql_query($result);
// Mostramos un mensaje de confirmación 
echo "Gracias por inscribirte.<br />"; 
echo "Ahora puedes <a href='login.php'>iniciar sesión</a>";
}
?>

Comprobando caracteres inválidos

El siguiente paso es comprobar que no existen caracteres no válidos que se puedan insertar en cualquiera de los campos. Si usamos los nuevos tipos de inputs nuevos en HTML5, (p.ej: type=”email”), ya tendremos una comprobación básica para el correo electrónico y la dirección de Paypal. Aquí sólo voy a hacer las comprobaciones para validar la contraseña introducida (con javascript), y para que no se introduzcan los datos en la BD si alguno de los campos está vacío.

Para empezar, voy a crear una carpeta en el mismo sitio donde tengo el resto de mis archivos, y la llamaré “functions”. Después, dentro de ella, creo un archivo “forms.js” en el cual incluiré todas las validaciones.

Para poner todo esto en funcionamiento, abriremos el archivo forms.js y dentro de él vamos a crear una función “ValidarPassword()” que valide las contraseñas. En él, deberemos encadenar varias sentencias IF/ELSE y usaremos un While para comprobar si la contraseña contiene espacios en blanco.

Esto será lo que nuestro archivo forms.js debe comprobar al enviar nuestro formulario:

  1. Comprobaremos si el valor del campo de contraseña “pw” y “pw2” coinciden. Si no coinciden mostraremos un aviso y retornaremos al formulario.
  2. Comprobamos si alguno de los campos de contraseña están vacíos
  3. Comprobamos que tenga al menos 6 caracteres (podremos cambiar el valor).
  4. Recorreremos la cadena en busca de espacios en blanco y retornaremos al formulario en caso de que existan.
  5. Si todo lo anterior está bien, daremos paso al código PHP para introducir los datos a la BD.
function ValidarPassword(){
 // Buscamos y guardamos los valores de los campos de contraseña
 var p1 = document.getElementById("pw").value;
 var p2 = document.getElementById("pw2").value;
 // Seteamos un par de variables de control.
 var espacios = false;
 var cont = 0;
 
 if (p1 != p2) { // Si las contraseñas no coinciden
   alert("Las contraseñas no coinciden.");
   return false; // No se ejecuta la validación
 } else if (p1.length == 0 || p2.length == 0) { // Si los campos de contraseña no contienen valores
       alert("Los campos de contraseña no pueden quedar vacios.");
       return false; // No se ejecuta la validación
     } else if (p1.length < 6 || p2.length < 6){ // Quiero que la contraseña tenga al menos 6 caracteres
           alert("Debes introducir una contraseña con un mínimo de 6 caracteres.");
           return false; // Si no se cumple, no se ejecuta la validación
         } else {
           // Vamos a comprobar que en los campos de contraseña no existan espacios
           // Podemos hacer esto mismo para el resto de campos si queremos
           while (!espacios && (cont < p1.length)){
             if (p1.charAt(cont) == " "){
               espacios = true; // Si hay un espacio, seteamos la variable a true
             }
             cont++;
           }
           if (espacios){ // Si la variable espacios es verdadera, hay espacios.
             alert("La contraseña no puede contener espacios en blanco.");
             return false; // No se ejecuta la validación
           } else {
             return true; // Si todo lo anterior está bien, dejamos que se valide el formulario. 
           }
        }
}

En nuestro formulario, deberemos añadir un par de cosas a nuestro código:

En la cabecera, entre nuestras etiquetas <head></head> introduciremos el siguiente código para enlazar a nuestro archivo .js.

<script src="functions/forms.js"></script>

En el formulario, añadiremos una etiqueta “onSubmit” que ejecutará la función cada vez que pulsemos el botón de registro.

<form action="singup.php" method="post" onSubmit="return ValidarPassword()">

Comprobar si el nombre de usuario ya existe en la base de datos.

Lo siguiente que vamos a hacer es comprobar si el nombre de usuario ya está en la base de datos. En el código de abajo, usamos una simple consulta MySQL para obtener una gran variedad de clientes. Entonces la guardamos en la variable $result.

Por último, con una sentencia WHILE pasamos por cada campo de la matriz $result, nombrando cada registro $row. Dentro de la declaración WHILE podemos comprobar si nuestro nombre de usuario y el nombre que tenemos en la base de datos ($row) coinciden.

Si lo hacen, creamos una variable y la hacemos verdadera (TRUE). De lo contrario, creamos la misma variable y la hacemos falsa (FALSE).

//Comprueba si el usuario ya existe 
$query = "SELECT username FROM clients"; 
$result = mysql_query($query) or die(mysql_error()); 
// Genera un array con los clientes 
while($row = mysql_fetch_array($result)){ 
 // Para cada registro, comprueba el nombre de usuario 
  if($row['username'] == $username){ 
   $usernameTaken = true; 
  }else{ 
   $usernameTaken = false; 
  } 
}

Ahora podemos añadir unas simples líneas a nuestro IF/ELSE para comprobar si hay nombres de usuario duplicados.

if($usernameTaken) { 
  echo "El usuario ya existe.<br />";
  echo "Vuelve <a href='singup.php'>atrás y escoge otro</a>."; 
}

El código de arriba dice: Si existe el nombre de usuario ($usernameTaken), decir que ya existe. Doy la opción de volver a recargar la página para rellenar de nuevo los campos.

Ahora haremos la parte ELSE. Si el usuario no existe en la base de datos, introduciremos estos en la tabla.

if($usernameTaken) {
  echo "El usuario ya existe.<br />";
  echo "Vuelve <a href='singup.php'>atrás y escoge otro</a>.";
} else {
  $result = "INSERT INTO clients (client_ID, username, password, email, paypal) VALUES ('', '$username', '$pw', '$email', '$paypal')";
  $sql = mysql_query($result); echo "Gracias por inscribirte.<br />"; echo "Ahora puedes <a href='login.php'>iniciar sesión</a>";
}

Nuestra página de registro final “singup.php”

<html> 
<head> 
<meta charset="ISO-8859-1" /> 
<title>Registrarse</title> 
<script src="functions/forms.js"></script> 
</head> 

<body> 

<?php 

require('db.php'); 

if(!empty($_POST)) { 
  if ($_POST["username"] == null 
   || $_POST["password"] == null 
   || $_POST["email"] == null 
   || $_POST["paypal"] == null){ 
     echo "Has dejado campos vacíos. Vuelve <a href='singup.php'>atrás y rellena el formulario.</a>"; 
  } else { 
     $username = mysql_real_escape_string($_POST["username"]);
     $password = mysql_real_escape_string($_POST["password"]);
     $pw = md5($password);
     $email = mysql_real_escape_string($_POST["email"]);
     $paypal = mysql_real_escape_string($_POST["paypal"]); 

     //Comprueba si el usuario ya existe 
     $query = "SELECT username FROM clients"; 
     $result = mysql_query($query) or die(mysql_error());

     // Genera un array con los clientes 
     while($row = mysql_fetch_array($result)){ 
       // Para cada registro, comprueba el nombre de usuario 
       if($row['username'] == $username){ 
          $usernameTaken = true; 
       } else { 
          $usernameTaken = false; 
       } 
     } 

     if($usernameTaken) { 
       echo "El usuario ya existe.<br />"; 
       echo "Vuelve <a href='singup.php'>atrás y escoge otro</a>."; 
     } else { 
       $result = "INSERT INTO clients (client_ID, username, password, email, paypal) VALUES ('', '$username', '$pw', '$email', '$paypal')"; 
       $sql = mysql_query($result); 
       echo "Gracias por inscribirte.<br />"; 
       echo "Ahora puedes <a href='login.php'>iniciar sesión</a>"; }
   } 
} else {
?>
<form action="singup.php" method="post" onSubmit="return ValidarPassword()">
 Nombre de usuario:<br />
 <input name="username" type="text" autofocus /><br />
 
 Contraseña:<br />
 <input id="pw" name="password" type="password" /><br /> 

 Repetir contraseña:<br /> 
 <input id="pw2" name="password" type="password" /><br />
 
 Email:<br />
 <input name="email" type="email" /><br />
 
 Dirección de Paypal:<br />
 <input name="paypal" type="email" /><br />
 
 <input type="submit" value="Registrarse" />
 </form>
<?php
}
?> 

</body> 
</html>

Como podréis comprobar, he añadido alguna cosa más al código, con la finalidad de hacerlo algo más presentable (las partes marcadas en naranja).

if(!empty($_POST)) { 
  if ($_POST["username"] == null 
   || $_POST["password"] == null 
   || $_POST["email"] == null 
   || $_POST["paypal"] == null){ 
     echo "Has dejado campos vacíos. Vuelve <a href='singup.php'>atrás y rellena el formulario.</a>"; 
  } else {

... // Código PHP
} else {

// Código HTML

<?php
}
?>

Con “!empty($_POST)” haremos que, si la variable $_POST está vacía, nos muestre el formulario. Si no lo está (si hemos pulsado el botón “Registrarse”), nos ejecutará el código PHP. Justo después, he puesto una comprobación para que todos los campos deban tener datos (no ser nulos). Si hay algún campo en blanco, damos la opción de volver atrás, si no, ejecutamos el resto del código. Hay muchas y mejores formas de hacer esto, pero yo voy a utilizar esta.

Conclusión

La seguridad es probablemente uno de los temas más aburridos en el desarrollo web, pero es uno de los más importantes. En la próxima parte veremos la forma de crear nuevos registros, las sesiones en PHP y la creación de una página de perfil para el cliente.

¡Un saludo!

Anuncios

Ayúdame a mejorar. Deja un comentario:

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s