====== 06 - PHP: Procesamiento de datos ======
Como hemos visto, la forma habitual de enviar datos a un API REST es en formato JSON. El servidor debe ser capaz de leer esos datos y tratarlos de forma adecuada. Además, la respuesta también tiene que estar en formato //JSON//, y llevar su correspondiente código de estado.
En este punto, usaremos //Postman// para hacer nuestras peticiones al servidor.
===== Leer datos de entrada =====
Supongamos que queremos enviar datos de un nuevo libro a nuestra aplicación para almacenarlo en nuestra bbdd. Vamos a crear un archivo PHP //recibir.php// que se encargará de la lectura de los datos. En principio, vamos a dejar el archivo en blanco (ya iremos modificándolo conforme avancemos en el tema).
Desde //Postman// podemos enviar los datos desde su pestaña //Body//, con formato //raw// - //JSON//:
{{ :clase:daw:dws:2eval:datos01.png?400 |}}
Una vez enviados, se ejecutará el //script// del archivo //recibir.php//. Lo que tenemos que hacer es leer esos datos. Aunque el método que hemos utilizado es //POST//, si mostramos el contenido de la variable //$_POST// vemos que está vacía (también si mostramos //$_REQUEST//). Ésto es así porque estamos recibiendo una cadena JSON como datos de entrada. Para recibir una cadena JSON, podemos usar [[https://www.php.net/manual/es/wrappers.php.php|php://input]] junto con la función [[https://www.php.net/manual/es/function.file-get-contents|file_get_contents]] que nos ayuda a recibir datos //JSON// como un archivo y leerlos en una cadena. Más tarde, podemos usar la función [[https://www.php.net/manual/es/function.json-decode|json_decode]] para decodificar la cadena //JSON//.
Por ejemplo, si queremos mostrar los datos que hemos recibido, podemos añadir el siguiente código a //recibir.php//:
$json = file_get_contents('php://input');
$data = json_decode($json);
var_dump($data);
{{ :clase:daw:dws:2eval:datos02.png?400 |}}
===== Enviar datos =====
En el punto anterior hemos visto como leer los datos de entrada, pero si te fijas en la respuesta no estamos devolviendo el formato que toca (JSON). Para eso, usamos la función [[https://www.php.net/manual/es/function.json-encode|json_encode]], la cual retorna la codificación JSON del valor dado:
$json = file_get_contents('php://input');
$data = json_decode($json);
echo json_encode($data);
{{ :clase:daw:dws:2eval:datos03.png?400 |}}
==== Enviar código de respuesta ====
Si te fijas en la parte de arriba a la derecha de la respuesta en //Postman//, verás que el código de respuesta es 200.
{{ :clase:daw:dws:2eval:datos04.png?400 |}}
Si queremos modificar el código enviado, tenemos que usar la función [[https://www.php.net/manual/es/function.http-response-code|http_response_code]] antes de enviar los datos. Por ejemplo, si queremos enviar un código 404:
http_response_code(404);
echo json_encode($data);
{{ :clase:daw:dws:2eval:datos05.png?400 |}}
De esta forma, podemos formatear la respuesta a nuestro gusto con los códigos de respuesta adecuados.
===== Tratar los datos =====
Vamos a volver a la versión original de nuestro archivo //recibir.php// (sin enviar ninguna respuesta por ahora):
$json = file_get_contents('php://input');
$data = json_decode($json);
En la variable $data ya tenemos los datos que nos ha pasado el cliente. Ahora, ya podemos tratarlos como queramos. Por ejemplo, vamos a insertar el nuevo libro en nuestra bbdd, y devolver el resultado de la operación.
include "conexion.php";
$json = file_get_contents('php://input');
$data = json_decode($json);
$sql = "INSERT INTO libros (titulo, autor, precio) VALUES ('$data->titulo', '$data->autor', $data->precio)";
$count = $pdo->exec($sql);
echo json_encode(["Resultado" => "Insertados $count libros"]);
El archivo //conexion.php// es igual que el visto hasta ahora, simplemente conecta con la bbdd. Además, el resultado de la operación lo hemos codificado como un //array// asociativo.
Si miras en tu bbdd, verás que se ha creado el nuevo libro.
===== Seguridad en el tratamiento de datos =====
A la hora de procesar los datos de una petición desde nuestro archivo PHP hay una regla de oro que nunca se debe olvidar: **Nunca hay que fiarse de los datos que vienen de un usuario o cualquier otra fuente externa**.
En temas anteriores vimos un ejemplo de ataque //SQL Injection// y como poder evitarlos. Tal y como está hecho nuestro //script//, nuestro servidor es vulnerable a ese tipo de ataques, con lo que deberíamos usar sentencias preparadas para intentar evitarlos.
Además, existen otro tipo de ataques igual o más peligrosos, como **XSS** (**Cross-site scripting**), que son una de las vulnerabilidades más explotadas en los sitios webs. Consiste en inyectar código malicioso en nuestra web mediante algún lenguaje del lado cliente (habitualmente Javascript).
Existen varios tipo de ataque //XSS//, pero hay tres que son los principales:
**No persistente** o **reflejado**: Un usuario envía datos al servidor a través, por ejemplo, de un formulario y el servidor genera una página dinámica usando esos datos. Si los datos contienen algún literal HTML, el servidor los inyectará en la página que le devuelve al usuario.
**Persistente** o **almacenado**: El código queda almacenado en el servidor (en una bbdd, por ejemplo), ejecutándose cada vez que un usuario accede a alguna página que muestre los datos. Por ejemplo, se puede inyectar código javascript en los comentarios de algún artículo de una tienda online. Cada vez que alguien accediera a ese artículo y cargara los comentarios se ejecutaría el código atacante. Este tipo de ataque es más peligroso que el anterior, ya que las víctimas potenciales serían cualquier usuario que ingresara en el sitio web.
**Basado en el DOM** o **XSS local**: En este caso son los //scripts// del cliente, el JavaScript por ejemplo en el navegador del usuario, el que construye elementos dinámicamente insertándolos en el DOM de la página. Con Javascript podemos acceder a ese árbol para insertar un elemento ",
"autor": "Umberto Eco",
"precio": 18.95
}
Nuestra aplicación, al no estar preparada, introduciría los datos en la bbdd. A partir de ahora, cada vez que mostráramos los datos en una web, se ejecutaría el código Javascript almacenado.
==== Prevenir ataques XSS ====
Como hemos dicho, nunca hay que fiarse de los datos que vienen de un usuario o cualquier otra fuente externa. Todos los datos deben ser validados en el servidor y escapados.
Afortunadamente, PHP (y todos los lenguajes de servidor) nos ofrecen herramientas sencillas para evitar este tipo de ataque (de todas formas, en el tema de seguridad nunca se puede estar del todo protegido. Siempre habrá alguna vulnerabilidad que pueda ser aprovechada por un atacante).
Para protegerse del //XSS// existen varias medidas que se pueden tomar, como **validación de datos**, **sanitización de datos** y **escapar las salidas de datos**.
=== Validación de datos ===
La validación de datos es el proceso de asegurarse que tu aplicación analiza el tipo de datos correctos. Si estas esperando un dato de tipo entero, debes rechazar cualquier otro tipo de datos.
Por ejemplo, si en nuestra bbdd existe un campo teléfono, deberíamos rechazar cualquier //string// que contenga caracteres que no sean dígitos. También podemos tener en cuenta la longitud que deberán tener esos dígitos (9, en este caso).
=== Sanitización de datos ===
En este caso, lo que hacemos es filtrar los datos de entrada. Podemos usar la función [[https://www.php.net/manual/es/function.strip-tags|strip_tags]] de PHP para realizar el filtrado. Esta función retira las etiquetas HTML y PHP de un string.
$nombre = strip_tags($_POST['nombre']);
$apellidos = strip_tags($_POST['apellidos']);
$edad = strip_tags($_POST['edad']);
También podríamos crearnos nuestras propio códgio para eliminar las expresiones no deseadas, aunque es más sencillo utilizar las que nos ofrece PHP.
=== Escapar la salida de datos ===
Otro método de prevención es escapar los valores a la hora de mostrarlos. PHP tiene una función que convierte caracteres especiales en entidades HTML: [[https://www.php.net/manual/es/function.htmlspecialchars|htmlspecialchars]].
$nombre = htmlspecialchars($_POST['nombre']);
$apellidos = htmlspecialchars($_POST['apellidos']);
$edad = htmlspecialchars($_POST['edad']);
Existen librerías desarrolladas por terceros que nos pueden ayudar a automatizar estas tareas.