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.
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:
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 php://input junto con la función 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 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);
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 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);
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.
Si queremos modificar el código enviado, tenemos que usar la función 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);
De esta forma, podemos formatear la respuesta a nuestro gusto con los códigos de respuesta adecuados.
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"]);
Si miras en tu bbdd, verás que se ha creado el nuevo libro.
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 <script> con código malicioso. Su código se ejecutará cuando se visualice la página en el navegador de la víctima.
Por ejemplo, si dejamos el código PHP tal y como está ahora (sin sentencias preparadas), alguien podría introducir los siguientes datos como nuevo libro:
{ "titulo": "<script>alert(\"Hola\")</script>", "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.
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.
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).
En este caso, lo que hacemos es filtrar los datos de entrada. Podemos usar la función 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.
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: 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.