Hoy · Hoja de ruta
- 1. Aprende: Cómo se sirve un fichero del disco y por qué eso es peligroso si no se hace bien.
- 2. Haz: Sirve desde tu servidor la web de las unidades anteriores.
- 3. Comprueba: No se puede pedir un fichero fuera de la carpeta pública.
Antes de empezar · 5 minutos, sin apuntes
- Hasta ahora, ¿quién servía tu web durante el desarrollo?
- ¿Cómo sabe el navegador que un fichero es CSS y no texto?
- ¿Qué pasaría si alguien pidiera
/../../.env?
Servir un fichero
import { readFile } from "node:fs/promises";
import path from "node:path";
const PUBLICO = path.join(aqui, "..", "publico");
const TIPOS = {
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".webp": "image/webp",
".svg": "image/svg+xml"
};
async function servirEstatico(ruta, respuesta) {
const relativa = ruta === "/" ? "/index.html" : ruta;
const destino = path.join(PUBLICO, relativa);
if (!destino.startsWith(PUBLICO)) {
return responderJson(respuesta, 403, { error: "Prohibido" });
}
try {
const contenido = await readFile(destino);
respuesta.writeHead(200, { "Content-Type": TIPOS[path.extname(destino)] ?? "application/octet-stream" });
respuesta.end(contenido);
} catch (error) {
if (error.code === "ENOENT") return responderJson(respuesta, 404, { error: "No encontrado" });
throw error;
}
}
La comprobación que no puede faltar
Un servidor de ficheros sin comprobar la ruta es un agujero
Si compones la ruta con lo que pide el cliente y no compruebas nada, una petición con tramos .. sale de tu carpeta pública y llega a cualquier fichero que el proceso pueda leer: tu .env, tu código, ficheros del sistema.
Se llama path traversal y es una de las vulnerabilidades más antiguas que existen. La defensa es la de arriba: resolver la ruta completa y comprobar que sigue estando dentro de la carpeta permitida. Después de resolver, nunca antes.
El tipo de contenido
Si sirves un CSS sin su cabecera, el navegador recibe texto y no aplica los estilos, sin decir nada claro. Con un módulo de JavaScript es peor: se niega a ejecutarlo por el tipo incorrecto. Es un fallo que parece de tu código y es de tus cabeceras.
Servidor de estáticos y API a la vez
if (ruta.startsWith("/api/")) return atenderApi(peticion, respuesta, url);
return servirEstatico(ruta, respuesta);
Con esto, tu proyecto entero se sirve desde un solo sitio: la web en / y los datos en /api/. Al proceder todo del mismo origen, desaparece el problema de CORS que viste en la UD4.
Tarea 12 · Tu web, servida por ti
- Copia el sitio de la UD4 a
publico/. - Sírvelo desde tu servidor, con
index.htmlpor defecto. - Resuelve el tipo de contenido por extensión.
- Protege contra rutas que salgan de la carpeta pública.
- Devuelve 404 con una página propia cuando el fichero no exista.
- Comprueba que el
fetchde la UD4 funciona ahora contra tu propia API.
Cierre de la semana 4
- Tienes un servidor propio que sirve web y API.
- Cada situación devuelve su código de estado.
- Los ficheros se sirven con su tipo correcto.
- No se puede salir de la carpeta pública.
Ver respuestas
1 · Comprobar que la ruta resuelta sigue dentro de la carpeta pública.
2 · Por su extensión, para escribir la cabecera de tipo de contenido.
3 · Porque cliente y API pasan a compartir origen.
Microprueba semanal 4 · 5–10 minutos
Individual, sin IA y sin apuntes.
- Nombra las tres cosas que se escriben en una respuesta HTTP, con un ejemplo de cada una.
- Di qué código de estado devolverías al crear, al no encontrar, ante datos inválidos y ante un fallo interno.
- ¿Qué comprobación evita que alguien pida un fichero fuera de la carpeta pública?