Una API de tickets de soporte acepta {"title": 7, "description": "<texto muy extenso>", "email": "alice@example.com"} sin verificar la estructura de la solicitud. Un paso posterior de formato falla debido al título numérico, mientras que un gestor de errores registra la descripción y el correo electrónico enviados. El atacante no necesitó una cuenta ni una cadena de exploits, solo una solicitud que cruzara el límite de la API con datos que el servicio trataba como fiables. La cuestión es dónde debe el servicio rechazar las entradas mal formadas, excesivamente grandes o inesperadas antes de que lleguen a la lógica de negocio o a los registros.
Utilizaremos un único servicio FastAPI a lo largo del curso. POST /tickets acepta un nuevo ticket, GET /tickets/{id} lee uno, POST /login autentica a un usuario y GET /me devuelve el usuario autenticado. Cada endpoint recibe datos controlados por un cliente —incluso cuando el cliente es un navegador de confianza en la actualidad—, por lo que cada uno requiere un límite explícito entre la entrada recibida y el estado interno.
La validación de entradas es la regla de límite que acepta únicamente datos que cumplen con el contrato del servicio. Para POST /tickets, una solicitud válida contiene un título de texto breve, una descripción con límites de tamaño y un correo electrónico con el formato requerido. Un JSON mal formado provoca un fallo aún más temprano, ya que el framework no puede construir el cuerpo de la solicitud a partir de una sintaxis inválida. El resultado útil no es simplemente una reducción de las excepciones; la entrada rechazada nunca llega al almacenamiento de tickets, a la generación de la respuesta ni a los registros de diagnóstico que podrían exponer datos personales.
El servicio presenta tres límites que conviene tener presentes durante el desarrollo: el análisis de la solicitud (parsing), la autenticación en POST /login y la generación de la respuesta. El primer límite es nuestro punto de partida, ya que todas las solicitudes no autenticadas llegan a él. Sigamos el recorrido de los campos controlados a través del servicio:
Este diagrama muestra que title, description y email provienen del exterior del servicio, mientras que el repositorio en memoria recibe únicamente los datos que han superado el límite de la solicitud.
Dado que el diagrama separa los datos del cliente de los datos del repositorio, POST /tickets requiere reglas en las que el controlador pueda basarse, en lugar de suposiciones dispersas por el código posterior. El contrato especifica los campos permitidos, sus tipos y sus límites. Un título podría admitir entre 1 y 120 caracteres, mientras que una descripción podría permitir hasta 4.000. Los límites actúan como controles de seguridad, ya que restringen el almacenamiento, el procesamiento, el volumen de registros y el tamaño de la respuesta antes de que un atacante pueda amplificarlos.
La normalización debe realizarse en el mismo límite cuando el servicio requiere una forma canónica. Podemos eliminar los espacios en blanco iniciales y finales de un título antes de almacenarlo, pero no debemos corregir silenciosamente un correo electrónico no válido ni truncar una descripción excesivamente larga. Corregir altera la intención del cliente; rechazar hace que el contrato sea evidente. Los validadores de campos de Pydantic pueden transformar los valores aceptados, pero deben devolver el resultado aceptado, y las reglas que requieren la entrada sin procesar deben contemplar entradas de cualquier tipo.
¿Qué debe devolver POST /tickets cuando el valor de email es not-an-email? El controlador no debe crear el ticket 42, y el cliente necesita una estructura de error estable en lugar de un seguimiento de pila interno. El siguiente modelo de solicitud aplica límites a los campos y registra el campo rechazado sin almacenar el correo electrónico rechazado en sí: