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.
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.