Skip to content
SP StackPractices
beginner

Validación de Input

Cómo validar input de usuarios de forma segura usando schemas, type checking y sanitización en Python, JavaScript y Java.

Temas: api

Visión general

La validación de input asegura que los datos que entran a tu aplicación cumplan con los formatos, tipos y restricciones esperados antes de procesarlos. Es la primera línea de defensa contra ataques de inyección, corrupción de datos y errores de aplicación.

Nunca confíes en el input del usuario. Valida en el límite — lo más cerca posible de la fuente — y falla rápido con mensajes de error claros.

Cuándo usarlo

Usa esta recipe cuando:

  • Aceptas datos de APIs, formularios o uploads de usuarios
  • Parseas datos externos (JSON, CSV, XML) antes de procesarlos
  • Aplicas reglas de negocio en payloads entrantes
  • Previenes ataques de inyección (SQL, XSS, command injection)
  • Conviertes y sanitizas strings no confiables a valores tipados

Solución

Python (Pydantic)

from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional

class UserCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    email: EmailStr
    age: int = Field(..., ge=0, le=150)
    bio: Optional[str] = Field(default=None, max_length=500)
    
    @validator('name')
    def name_must_not_be_blank(cls, v):
        if not v.strip():
            raise ValueError('name cannot be blank')
        return v.strip()

# Uso
try:
    user = UserCreate(name="Ada Lovelace", email="ada@example.com", age=36)
    print(user.dict())
except ValueError as e:
    print(f"Validation error: {e}")

JavaScript (Zod)

const { z } = require('zod');

const UserCreate = z.object({
  name: z.string().min(1).max(100).trim(),
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  bio: z.string().max(500).optional(),
});

// Uso
try {
  const user = UserCreate.parse({
    name: 'Ada Lovelace',
    email: 'ada@example.com',
    age: 36,
  });
  console.log(user);
} catch (e) {
  console.error('Validation error:', e.errors);
}

// Safe parse (retorna resultado en lugar de lanzar)
const result = UserCreate.safeParse({ name: '', email: 'bad' });
if (!result.success) {
  console.error(result.error.issues);
}

Java (Jakarta Validation / Hibernate Validator)

import jakarta.validation.*;
import jakarta.validation.constraints.*;

public class UserCreate {
    @NotBlank
    @Size(min = 1, max = 100)
    private String name;
    
    @NotBlank
    @Email
    private String email;
    
    @Min(0)
    @Max(150)
    private int age;
    
    @Size(max = 500)
    private String bio;
    
    // Getters y setters omitidos
}

// Uso
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

UserCreate user = new UserCreate("Ada Lovelace", "ada@example.com", 36, null);
Set<ConstraintViolation<UserCreate>> violations = validator.validate(user);

for (ConstraintViolation<UserCreate> v : violations) {
    System.out.println(v.getPropertyPath() + ": " + v.getMessage());
}

Explicación

  • Validación schema-first: Define la forma esperada una vez, valida en todas partes
  • Coerción de tipos: Librerías como Pydantic y Zod pueden hacer cast seguro de strings a números, booleans y fechas
  • Validadores custom: Extiende schemas con lógica de negocio (ej. fortaleza de contraseña, checks de unicidad)
  • Agregación de errores: Recolecta todos los errores de validación en una sola respuesta para mejor UX

Mejores prácticas

  • Valida temprano: Rechaza input inválido en el límite de la API, no profundo en la capa de servicio
  • Usa schemas estrictos: Prefiere allowlists explícitas sobre tipos catch-all permisivos
  • Sanitiza después de validar: Escapa HTML, trim whitespace, normaliza Unicode
  • Retorna errores estructurados: Retorna errores a nivel de campo para que la UI los muestre inline
  • Nunca confíes solo en validación frontend: Siempre re-valida en el servidor
  • Loguea fallos de validación: Monitorea patrones que pueden indicar ataques

Errores comunes

  • Confiar en validación client-side como única defensa
  • Usar regex para validación compleja cuando una librería de schemas está disponible
  • Permitir coerción de tipos implícita sin reglas explícitas
  • Retornar errores crudos de base de datos en lugar de mensajes de validación amigables
  • Validar demasiado tarde, después de mutación parcial de estado

Preguntas frecuentes

P: ¿Debería validar en el controller o en la capa de servicio? R: Valida en el límite (controller / capa de API). La capa de servicio debe asumir datos limpios y validados.

P: ¿Cómo manejo validación de objetos anidados y arrays? R: Las tres librerías soportan schemas anidados. Define modelos hijos y reférencialos en modelos padres.

P: ¿Cuál es la diferencia entre sanitización y validación? R: La validación verifica si el input cumple restricciones. La sanitización limpia el input removiendo o escapando caracteres no deseados.