Análisis seguro de CSV y JSON: protege las subidas contra entradas maliciosas
El parseo seguro de CSV y JSON te ayuda a evitar inyección de fórmulas, filas malformadas y explotaciones de memoria cuando los usuarios suben archivos a tu app.

¿Por qué son riesgosos los CSV y JSON suministrados por usuarios?
Las subidas de usuarios no son confiables. Esto suena obvio, pero lo cambia todo. Los datos internos suelen seguir tus reglas porque tu propio código los generó. Los archivos subidos vienen de herramientas desconocidas, ajustes desconocidos y, a veces, con intenciones dudosas.
CSV y JSON también parecen inofensivos porque son "solo texto". Pero el texto aún puede desencadenar errores, crear registros malos o colar contenido que otras partes de tu sistema ejecutarán. Incluso un archivo pequeño puede hacer daño si toca el punto débil correcto, como un caso límite del parser o una fórmula de hoja de cálculo.
Esto se convierte en problemas reales de negocio de formas comunes: importaciones que fallan y generan caídas, filas malas que contaminan silenciosamente la base de datos, exportaciones que desencadenan inyección de fórmulas al abrirse en hojas de cálculo, y subidas grandes que ralentizan el sistema si todo se carga en memoria.
"Parseo seguro" no significa "mi código puede leer el archivo". Significa que controlas lo que pasa cuando el archivo es raro, hostil o simplemente más grande de lo esperado. En la práctica, se reduce a límites (tamaño, tiempo, conteo de filas), lectura predecible (streaming en vez de cargar todo a la vez) y validar cada registro antes de que toque la base de datos.
Un escenario realista es una importación de contactos subida por un cliente. El archivo puede contener una sola celda con una cadena de 5 MB, un campo JSON con anidamiento inesperado o un nombre que empieza por =HYPERLINK(...). Si tratas la subida como entrada confiable, puedes acabar con downtime, limpieza de datos y un incidente de seguridad.
Las principales formas de fallo para planear
Las subidas de usuarios fallan de unas formas previsibles. Nómbralas desde el principio y el parseo seguro pasa a ser aburrido en vez de arriesgado.
CSV malformado es el problema clásico. Los archivos reales a menudo tienen comillas rotas, comas extra, números de columnas dispares u codificaciones raras que convierten caracteres en basura. Algunos parsers intentan adivinar lo que el usuario quiso decir. Eso puede desplazar columnas silenciosamente y corromper tus datos, o hacer que un trabajo de importación falle cuando una sola fila está muy malformada.
Inyección de fórmulas en CSV es más sutil. Las hojas de cálculo tratan las celdas que empiezan con =, +, - o @ como fórmulas. Si luego exportas datos y alguien lo abre en Excel o Google Sheets, un atacante puede plantar una celda que ejecute una fórmula. El peligro suele estar aguas abajo: en el flujo humano.
Trampas en JSON aparecen de otra forma. Objetos muy anidados pueden alcanzar límites de recursión o disparar el uso de CPU. Arrays enormes pueden convertir una "subida pequeña" en minutos de trabajo. Las claves duplicadas son otro truco: algunos parsers conservan el primer valor, otros el último. Los atacantes pueden usar esa ambigüedad para eludir validaciones.
Explosiones de memoria son la forma de fallo más simple. Leer todo el archivo en memoria (o construir un objeto completo antes de validar) convierte subidas grandes en timeouts y caídas. Una subida de 50 MB puede crecer mucho más una vez parseada.
Un ejemplo práctico: una subida "contacts.csv" parece bien, pero una fila tiene una comilla sin cerrar. Tu parser desplaza columnas y ahora el campo email contiene partes de otra columna. O un "contacts.json" incluye un contacto con un array de notas masivo y el uso de memoria se dispara.
Empieza por límites estrictos antes de parsear
La mayoría de errores en subidas ocurren antes de que el parser pueda ayudar. Si aceptas cualquier cosa que parezca una hoja de cálculo o "algo de JSON", invitas a casos límite que nunca probaste.
Decide qué soportas realmente. Si una función solo necesita una tabla simple, acepta CSV y rechaza JSON por completo (y viceversa). Soportar menos formatos elimina muchas esquinas raras.
Luego establece límites duros que coincidan con tu uso real. El tamaño de archivo no basta. Un archivo pequeño puede contener millones de campos diminutos, y un archivo de tamaño normal puede expandirse en memoria si lo cargas todo.
Límites que vale la pena imponer
Para importaciones típicas como contactos, un pequeño conjunto de guardarraíles hace la mayor parte del trabajo:
- Bytes máximos (basados en los límites de tu producto)
- Filas máximas y columnas máximas
- Longitud máxima de campo (por celda o cadena JSON)
- Profundidad máxima de anidamiento para JSON
- Límite de tiempo por parseo
La codificación es otro pie en falso común. Prefiere UTF-8, maneja un BOM UTF-8 y rechaza archivos que no puedan decodificarse limpiamente. La decodificación silenciosa "a mejor esfuerzo" puede convertir un byte malo en un delimitador desplazado que rompe cada fila.
Rechaza contenido inesperado temprano. Para CSV, valida el encabezado exactamente (o comparándolo con una pequeña lista permitida) antes de procesar filas. Para JSON, confirma la forma de nivel superior (objeto vs array) y las claves requeridas antes de tocar el resto.
Registra rechazos sin almacenar contenido sensible. Guarda metadatos y razones (tamaño de archivo, conteo de filas, primera línea con error, regla que falló), pero evita conservar la subida en bruto.
Defenderse contra la inyección de fórmulas en CSV
CSV parece inocuo, pero las hojas de cálculo tratan algunas celdas como fórmulas. Si un atacante sube un CSV y luego lo exportas para que alguien lo abra, un valor que empiece como fórmula puede ejecutarse. Esto es la inyección de fórmulas en CSV.
El problema común no es que tu parser falle. El peligro es lo que pasa después, cuando un humano abre los datos y la hoja los evalúa.
Una regla práctica: guarda el valor original para almacenamiento y auditoría, pero crea una copia segura para exportaciones cada vez que el valor pueda acabar en un CSV que alguien abra. Así no pierdes lo que el usuario envió y no entregas un archivo cargado a tu equipo.
Al construir la copia segura para exportación, trata una celda como peligrosa si, tras quitar espacios y tabs iniciales, comienza con cualquiera de estos caracteres: =, +, - o @. Si es peligrosa, neutralízala anteponiendo una sola comilla (') antes del valor en el CSV exportado. Muchas hojas de cálculo entonces mostrarán el texto pero no lo ejecutarán como fórmula.
Cuidado con entradas engañosas. Los atacantes suelen añadir espacios iniciales, tabs o caracteres ocultos para que la celda parezca normal pero aún se evalúe como fórmula. Decide qué caracteres iniciales consideras ignorables (al menos espacios y tabs) y aplica la comprobación a la versión limpiada.
Prueba con cargas que suelen colarse:
=HYPERLINK(\"example\",\"click\")+SUM(1,1)-2+3(parece matemáticas)@SUM(1,1)\\t=1+1(espacio o tab al inicio)
Hacer el parseo de JSON predecible mediante validación
El JSON suministrado por usuarios no es solo datos. Es entrada no confiable y quieres que se comporte igual cada vez. El objetivo es simple: rechazar sorpresas temprano y aceptar solo la forma exacta que tu app espera.
Empieza validando la estructura antes de usar valores: campos requeridos, tipos correctos y un conjunto limitado de valores permitidos. Si tu endpoint espera algo como { "email": string, "role": "admin"|"member" }, no aceptes números, arrays u objetos extra "porque quizá aún funcionen". Así es como los casos límite llegan a producción.
Pon límites duros en JSON que sea costoso procesar
Un archivo JSON puede ser pequeño en bytes pero caro de parsear, como arrays muy anidados o cadenas gigantes. Fija límites por adelantado:
- Profundidad máxima de anidamiento
- Longitud máxima de cadena por campo (especialmente notas, biografías y metadata)
- Longitud máxima de array
- Máximo total de claves por objeto
Estas comprobaciones mantienen el parseo predecible y protegen memoria y CPU.
Trata las claves duplicadas como un problema
Los payloads JSON pueden contener claves duplicadas y los parsers las manejan de forma distinta (gana la primera, gana la última o indefinido). Esa ambigüedad puede usarse para eludir validaciones. En la mayoría de los casos, rechaza payloads con claves duplicadas para que un atacante no pueda esconder un valor malo detrás de uno bueno.
También evita que el parser adivine tipos "útiles". Si necesitas una cadena, mantenla cadena. No conviertas "00123" en número ni interpretes fechas implícitamente. Cambia el significado y puede romper lógica posterior.
Cuando la validación falla, devuelve errores claros y seguros para el usuario, como contacts[12].email must be a valid email address. No muestres trazas de pila, detalles SQL o información interna.
Paso a paso: parseo en streaming que no tumbará tu app
Los crashes suelen ocurrir porque el servidor intenta ser "útil" y carga toda la subida en memoria. El patrón más seguro es: fija límites primero y luego procesa pequeños fragmentos a la vez.
Empieza con un control rápido antes de parsear. Comprueba el tipo de contenido reportado, pero no confíes en él. Impone un tamaño máximo de archivo y rechaza archivos comprimidos a menos que puedas inspeccionarlos con seguridad. También fija un límite de tiempo para que una subida lenta no bloquee un worker.
Un flujo práctico en streaming
Un flujo sencillo que funciona tanto para CSV como para JSON:
- Preverifica límites: bytes máximos, codificaciones permitidas y un máximo de filas u objetos.
- Haz streaming desde disco o desde el cuerpo de la petición. No llames a "leer todo" ni construyas una cadena completa en memoria.
- Parsea registro por registro y para cuando alcances límites duros (filas, campos por fila, profundidad máxima para JSON, longitud máxima de cadena).
- Valida cada registro sobre la marcha y recoge solo una muestra pequeña de errores (por ejemplo, los primeros 20 problemas) además de contadores.
- Escribe datos aceptados de forma incremental: insertos por lotes o empuja registros válidos a una cola para procesarlos después.
No intentes "arreglar" la estructura rota mientras haces streaming. Si el parser informa entrada malformada, para y falla rápido. Las importaciones parciales están bien solo si tu producto lo explica claramente.
Para una importación amigable, devuelve un resumen: cuántos registros se aceptaron, cuántos se rechazaron y una pequeña lista de ejemplos de errores con números de línea (o rutas JSON). Por ejemplo: "2,431 importados, 17 rechazados. Problemas principales: email faltante, fecha inválida, columnas extra."
Reglas de validación que mantienen los datos malos fuera
Parsear es solo el primer paso. La verdadera ganancia en seguridad y calidad viene de tratar cada subida como no confiable y chequearla contra un contrato claro antes de tocar la base de datos.
Escribe qué significa "válido" para tu app. Manténlo pequeño y específico, e impónlo igual para columnas CSV y campos JSON. Un buen contrato suele cubrir campos permitidos, campos requeridos vs opcionales, reglas de tipo y formato (email, teléfono, fecha ISO, moneda), límites de rango y límites de longitud.
Las listas permitidas importan porque impiden que campos sorpresa se cuele, como una clave isAdmin inesperada en JSON o una columna role extra en CSV. Si un encabezado o clave no está en la lista, rechaza el archivo o ignora el campo explícitamente y regístralo, pero no lo aceptes silenciosamente.
La normalización debe ser cuidadosa y predecible. Recortar espacios y convertir TRUE a true está bien, pero evita conversiones que cambien el significado. Para fechas, elige un formato aceptado (o una lista corta), normaliza a una salida única y sé consistente.
Los mensajes de error deben ayudar al usuario a arreglar el archivo rápido. En lugar de "entrada inválida", devuelve algo como: "Fila 17, campo email: se esperaba [email protected]." Para JSON, señala una ruta: "contacts[3].phone falta."
Decide de antemano cuán estrictos quieres ser. Todo o nada es lo más seguro para importaciones que deben ser consistentes. El éxito parcial puede ser mejor para listas de contactos, pero necesita reglas claras (qué se rechaza, cuántos errores devuelves y qué almacenas).
Errores comunes y trampas a evitar
La mayoría de bugs en subidas no son una gran vulnerabilidad. Son una cadena de pequeñas suposiciones.
Una trampa común es parsear todo primero y solo luego ejecutar las comprobaciones. Para cuando la validación falla, los datos malos pueden ya estar en memoria, escritos en una tabla temporal o pasados a lógica de negocio. Trata la validación como parte del parseo: rechaza temprano y deja de leer en cuanto sepas que el archivo no es aceptable.
Otro error fácil es confiar en el nombre del archivo. Alguien puede subir un archivo llamado contacts.csv que en realidad sea otra cosa, o un CSV tan malformado que el parser se comporte de forma extraña. Inspecciona el contenido (encabezados, delimitadores, primeros bytes) y aplica una forma permitida pequeña antes de comprometerte a procesarlo.
Algunas trampas que vuelven a aparecer:
- Dejar que una librería detecte tipos sin límites. Adivinar puede convertir entradas raras en números enormes, fechas o valores
NaN. - Re-exportar CSV crudo sin protección contra fórmulas. Almacenar
=HYPERLINK(...)es una cosa; volver a exportarlo para que un compañero lo abra es cuando se vuelve peligroso. - Cargar todo el archivo para "obtener mejores errores". Una sola subida sobredimensionada puede causar picos de memoria y timeouts.
- Devolver reportes masivos de errores. Listar cada fila mala de un archivo enorme puede crear un segundo problema de memoria y filtrar fragmentos sensibles.
Un ejemplo realista: importas un "leads.csv" y devuelves un error detallado por fila. Un atacante sube un archivo enorme con pequeños fallos en cada línea. Tu servidor pasa minutos recopilando errores, construye una respuesta de varios megabytes y se queda sin tiempo.
Lista de comprobación rápida para subidas CSV y JSON
Si quieres parseo seguro de CSV y JSON, asume que el archivo es hostil. La mayoría de bugs en subidas no son ataques sofisticados. Son entradas simples que tu código no esperaba: archivos enormes, codificaciones raras o campos que parecen inofensivos pero activan comportamientos en otras herramientas.
Mantén una lista corta como control antes de que los datos lleguen a tu base:
- Fija límites duros: bytes máximos, filas u objetos máximos, columnas o campos máximos y (para JSON) profundidad máxima de anidamiento.
- Parsea en streaming para que una subida no pueda disparar la memoria.
- Valida estructura y valores: campos requeridos, tipos, límites de longitud, enums permitidos y formatos de fecha/número.
- Neutraliza la inyección de fórmulas en CSV al exportar o re-guardar datos.
- Falla cerrado: rechaza ante errores de parseo y devuelve mensajes pequeños y claros.
Para JSON, "JSON válido" no basta. Un archivo grande con arrays muy anidados puede ser válido y aun así arruinar el rendimiento. Los límites de profundidad, límites por campo y una validación de esquema estricta hacen que el parseo sea predecible.
Una vez en producción, monitoriza lo básico: timeouts de parseo y parses lentos, tasa de rechazos por motivo y filas u objetos promedio por subida. Esos indicadores te dicen dónde ajustar límites y UX.
Un ejemplo realista: importación de contactos que se mantiene segura
Un fundador añade un paso "Importar contactos" al flujo de registro. Los usuarios pueden subir un CSV de Excel o un JSON exportado desde otra herramienta. El objetivo es simple: crear registros de contacto (nombre, email, empresa) y saltarse todo lo inseguro.
Un día llega un CSV con esto en la columna de nombre:
=HYPERLINK(\"example\",\"Click me\")
Si tu app luego exporta esos contactos a CSV para que un compañero los abra, esa celda puede ejecutarse como fórmula. La solución no es "sanitizar después". Para cualquier campo que pueda volver a escribirse en CSV, o rechazas valores que empiecen por =, +, - o @, o guardas una versión segura para exportación (por ejemplo, anteponiendo un apóstrofo) y mantén el original fuera de las exportaciones.
En otro caso, alguien sube un JSON grande. Si tu servidor lo lee y parsea todo en memoria de una vez, puede congelarse o caer. En su lugar, impone un límite de tamaño, parsea en streaming, procesa un contacto a la vez y para pronto cuando se excedan los límites (max registros, longitud de campo, profundidad máxima).
Lo que ve el usuario importa. La página de importación debe ofrecer un resumen claro: cuántos se importaron, cuántos se omitieron y una muestra pequeña de razones con números de fila o rutas JSON. Detrás, los logs deben ayudarte a depurar sin almacenar datos personales. Registra contadores, números de fila, códigos de error y huellas no identificativas para detectar patrones sin guardar valores en bruto.
Próximos pasos: endurece tu pipeline y pide ayuda si la necesitas
Trata las subidas como una integración externa. Construye un pequeño conjunto de guardarraíles que puedas verificar.
Audita los endpoints que aceptan archivos. Busca límites de tamaño claros, timeouts, parseo en streaming y lugares donde el archivo se lee dos veces o se convierte en una gran cadena antes de parsear.
Mantén un pequeño paquete de pruebas que ejecutes en cada cambio: un CSV malformado, un CSV que pruebe comportamiento de inyección de fórmulas, un JSON muy anidado, un JSON con tipos erróneos y campos faltantes, y un archivo válido grande cerca del límite de tamaño. Esos pocos archivos detectan la mayoría de regresiones.
Si trabajas con una base de código generada por IA (especialmente prototipos de herramientas como Lovable, Bolt, v0, Cursor o Replit), haz una pasada extra para buscar límites faltantes, lecturas completas de archivo y logging bruto. Si quieres una segunda opinión, FixMyMess (fixmymess.ai) ayuda a equipos a diagnosticar y reparar flujos de importación generados por IA, incluyendo añadir límites, validación y manejo de exportaciones más seguro.
Preguntas Frecuentes
¿Por qué las subidas CSV y JSON son riesgosas si son solo texto?
Trata cada subida como entrada no confiable. Incluso “solo texto” puede desencadenar casos límite del parser, crear registros erróneos o causar problemas después, cuando los datos se exportan y se abren en software de hojas de cálculo.
¿Debería intentar “autocorregir” archivos CSV malformados?
El parseo estricto evita desplazamientos silenciosos de datos. Si intentas adivinar lo que el usuario “quiso decir”, una comilla sin cerrar o una coma extra puede mover valores a columnas equivocadas y corromper la base de datos sin que te des cuenta.
¿Qué límites debo aplicar antes de parsear un archivo subido?
Fija límites antes de parsear: bytes máximos, filas/columnas máximas y longitud máxima de campo. El tamaño por sí solo no basta porque un archivo modesto puede contener campos enormes o expandirse mucho al parsearse.
¿Cómo evito que las subidas me tumben la aplicación por desbordes de memoria?
Procésalo en streaming y valida mientras avanzas. Si lees todo el archivo en memoria primero, una subida grande (o una pequeña que se expande al parsearse) puede disparar la memoria y tumbar el worker o provocar timeouts.
¿Qué es la inyección de fórmulas en CSV y cuándo importa realmente?
Sucede cuando un valor que empieza por =, +, - o @ se exporta y alguien lo abre en Excel o Google Sheets, donde puede evaluarse como fórmula. El riesgo suele aparecer después, en un flujo humano, no durante la importación.
¿Cuál es la forma más simple de manejar la inyección de fórmulas en CSV al exportar?
Conserva el valor original para auditoría, pero genera una copia segura para cualquier CSV que alguien vaya a abrir en una hoja de cálculo. Una práctica común es, tras quitar espacios/tabs iniciales, prefijar un apóstrofo (') si comienza con un carácter de fórmula.
¿Cómo hago que el parseo de JSON sea predecible y seguro?
Valida exactamente la forma que esperas antes de usar cualquier valor, y rechaza las sorpresas tempranamente. Pon límites estrictos en profundidad de anidamiento, longitud de cadenas, longitud de arrays y claves totales para que “JSON válido” no sea caro de procesar.
¿Debería rechazar JSON con claves duplicadas?
Como los parsers difieren en cuál duplicado mantienen (el primero o el último), un atacante puede aprovechar esa ambigüedad para eludir validaciones. Lo más seguro es rechazar objetos JSON que contengan claves duplicadas.
¿Qué mensajes de error debería mostrar cuando una importación falla la validación?
Devuelve un resumen claro y pequeño que el usuario pueda corregir: en qué fila/ruta falló y por qué. Evita incluir contenido sensible, trazas de pila o reportes masivos por fila que puedan ser otro problema de rendimiento.
¿Cuáles son los errores más comunes en código de importación generado por IA y cómo puede ayudar FixMyMess?
Busca lecturas de archivo completas, falta de límites de tamaño/tiempo, registros brutos del contenido y exportaciones que vuelven a guardar datos de usuarios en CSV sin protección de fórmulas. Si el código fue generado por IA y falla en producción, FixMyMess puede auditarlo y reparar límites, streaming, validación y exportación.