Problema y alcance
Una plataforma de pedidos multi-inquilino expone un endpoint de búsqueda interno. El inquilino del usuario autenticado está disponible en la sesión del servidor. La solicitud puede contener el correo electrónico de un cliente, una lista de estados de pedidos, un campo de ordenamiento, una dirección de ordenamiento y un límite de resultados. Una revisión encuentra código estructurado de esta manera:
const sql = `
SELECT id, customer_email, status, total_cents, created_at
FROM orders
WHERE tenant_id = '${tenantId}'
AND customer_email = '${input.email}'
AND status IN (${input.statuses.join(",")})
ORDER BY ${input.sort} ${input.direction}
LIMIT ${input.limit}
`Explique cómo rediseñaría este límite de consulta para que el texto controlado por un atacante no pueda alterar la gramática SQL. Cubra valores, filtros opcionales, parámetros de lista, identificadores como columnas de ordenamiento, procedimientos almacenados, vías de escape para consultas sin procesar (raw query escape hatches) en ORM, privilegios de base de datos, registro (logging), pruebas y datos previamente almacenados que luego se reutilizan en SQL dinámico.
La regla central es que el código SQL debe estar determinado por código de aplicación de confianza, mientras que los datos externos llegan a la base de datos a través de una interfaz de vinculación de parámetros (parameter binding) del lado del servidor. La validación de entradas y el privilegio mínimo agregan barreras útiles, pero no transforman una concatenación de cadenas en una consulta segura.
Esta es una pregunta de backend porque las habilidades decisivas son la construcción de consultas, el comportamiento del controlador (driver) de la base de datos, la autorización en el límite del servicio, los permisos de la base de datos y la verificación en producción. El lenguaje de implementación es secundario.
Qué evalúa el entrevistador
La primera señal es si el candidato identifica cada límite gramatical en lugar de limitarse a decir “use una sentencia preparada (prepared statement)”. Valores como los IDs de inquilinos, direcciones de correo electrónico, estados y límites deben vincularse como datos. Los nombres de tablas, nombres de columnas, palabras clave de SQL y direcciones de ordenamiento generalmente no se pueden proporcionar mediante un marcador de posición de valor ordinario, por lo que requieren un rediseño de la consulta o una lista permitida gestionada por el servidor que asigne un pequeño enum público a tokens SQL fijos.
La segunda señal es distinguir la prevención de inyecciones de la autorización. Un tenant_id parametrizado sigue siendo inseguro para el aislamiento de inquilinos si proviene del cuerpo de la solicitud y el emisor puede elegir otro inquilino. El servicio debe derivar el inquilino a partir de la entidad principal autenticada e incluirlo en cada consulta relevante. La parametrización evita que un valor altere la estructura de la consulta; no demuestra que el emisor tenga permiso para acceder a ese valor.
La tercera señal es reconocer los límites de ejecución ocultos. Un ORM es seguro solo mientras el código utilice sus APIs parametrizadas correctamente. Los helpers de consultas directas (raw queries), los filtros construidos mediante cadenas, las herramientas de migración, los trabajos de generación de reportes y los procedimientos almacenados que ejecutan SQL dinámico pueden recrear la misma falla. Los datos almacenados de forma segura hoy también pueden convertirse en una fuente de inyección de segundo orden si otro trabajo los concatena posteriormente en SQL.
La señal final es la verificación por capas. Una respuesta sólida combina revisión de código, vinculación del lado del servidor, identificadores en listas permitidas, roles de base de datos con privilegios mínimos, manejo seguro de errores, pruebas de integración con cadenas hostiles, aserciones de aislamiento de inquilinos y monitoreo de errores de base de datos. No presenta un firewall de aplicaciones web (WAF) ni el escape manual como la reparación principal.
Preguntas aclaratorias antes de responder
- ¿Qué base de datos y controlador están desplegados? La sintaxis de los marcadores de posición, la vinculación de arreglos, las utilidades de entrecomillado de identificadores y el comportamiento de las sentencias preparadas varían. El principio de diseño es portable, pero la API exacta debe coincidir con el controlador desplegado.
- ¿La solicitud proporciona
tenantId? Si es así, ignore ese campo para la autorización y derive el inquilino a partir del contexto autenticado del servidor. El acceso administrativo entre inquilinos necesita una ruta separada y explícitamente autorizada. - ¿Qué filtros son opcionales? Los predicados opcionales deben agregarse a partir de fragmentos SQL fijos mientras sus valores se mantienen como parámetros. Una API genérica de tipo “adjuntar cualquier campo y operador” amplía en gran medida la superficie gramatical.
- ¿Qué campos de ordenamiento se requieren realmente? Si el producto solo necesita la fecha de creación y el monto total, exponga esas dos claves públicas. No acepte expresiones de columna arbitrarias, funciones, intercalaciones (collations) o cláusulas de orden separadas por comas.
- ¿El endpoint puede buscar múltiples estados o IDs? Utilice la funcionalidad de arreglos del controlador de base de datos, un parámetro de arreglo tipado o un conjunto generado de marcadores de posición. Nunca una valores sin procesar dentro del texto SQL.
- ¿Aparece alguna API de consulta directa de ORM en la ruta? Inspeccione tanto los métodos explícitamente inseguros como los helpers de plantillas “seguros” para confirmar si los valores son vinculados por el controlador o interpolados previamente en una cadena.
- ¿Los procedimientos almacenados construyen SQL dinámico? Un procedimiento no es automáticamente seguro. Sus parámetros deben permanecer como datos cuando el procedimiento invoca SQL; las rutas dinámicas estilo
EXECrequieren la misma revisión. - ¿Qué privilegios tiene el rol de la aplicación? Un endpoint de lectura normalmente no debería conectarse con propiedad del esquema, DDL o permisos sobre tablas no relacionadas. Separe las credenciales operativas y de migración de las credenciales de tiempo de ejecución.
- ¿Qué evidencia se requiere antes del lanzamiento? Defina el corpus de entradas hostiles, las comprobaciones de aislamiento de inquilinos, el inventario de consultas directas, la verificación de roles de base de datos y las señales de error en producción antes de modificar el código.
Marco de respuesta en 30 segundos
“Haría que el contexto autenticado del servidor fuera la fuente de tenantId, y luego vincularía cada valor de datos a través del controlador de base de datos: inquilino, correo electrónico, arreglo de estados y límite. Las columnas y direcciones de ordenamiento son gramática, por lo que asignaría dos valores de un enum público a tokens SQL fijos administrados por el servidor y rechazaría todo lo demás. Haría un inventario de llamadas a consultas directas de ORM y procedimientos almacenados porque pueden reintroducir SQL construido mediante cadenas, y parametrizaría los datos almacenados nuevamente cada vez que alcancen un límite de ejecución posterior. Luego reduciría el rol de base de datos en tiempo de ejecución, devolvería errores genéricos al cliente, monitorearía fallas internas detalladas y ejecutaría pruebas de integración demostrando que las cadenas hostiles se mantienen como literales y nunca pueden devolver filas de otro inquilino”.
Análisis detallado paso a paso
Paso 1: Identificar código, datos y fuentes de autorización
Clasifique cada parte de la consulta original:
| Parte de la consulta | Tipo | Fuente segura |
|---|---|---|
SELECT, tabla, predicados | Gramática SQL | Código de aplicación estático |
| ID de inquilino | Datos y alcance de autorización | Contexto autenticado del servidor |
| Correo electrónico, estados, límite | Datos | Parámetros vinculados del controlador |
| Columna de ordenamiento | Identificador SQL | Lista permitida del servidor |
| Dirección de ordenamiento | Palabra clave SQL | Lista permitida del servidor |
El ejemplo inseguro permite que cadenas derivadas de la solicitud participen en el análisis sintáctico (parsing). Comillas, comentarios, operadores o expresiones adicionales pueden alterar la sentencia antes de que la base de datos sepa qué caracteres debían ser datos. Verificar algunas subcadenas sospechosas no restablece el límite porque SQL tiene sintaxis, codificaciones, comentarios, funciones y comportamientos de analizador sintáctico específicos de cada dialecto.
Comience desde cada lugar que ejecute SQL, no solo este endpoint. Busque APIs de consultas directas, cadenas de plantillas, concatenación de cadenas alrededor de métodos de consulta, ejecución dinámica de procedimientos, filtros de reportes y constructores de consultas (query builders) que acepten nombres de campos u operadores. Rastree los wrappers hasta que pueda demostrar qué llega al controlador como texto SQL y qué llega como una colección de parámetros.
Paso 2: Vincular cada valor en el servidor
Una implementación en TypeScript estilo PostgreSQL puede mantener los valores separados:
const SORT_COLUMNS = {
createdAt: "o.created_at",
total: "o.total_cents",
} as const
const SORT_DIRECTIONS = {
asc: "ASC",
desc: "DESC",
} as const
interface OrderSearchInput {
email: string | null
statuses: string[] | null
sort: string
direction: string
limit: number
}
async function findOrders(authenticatedTenantId: string, input: OrderSearchInput) {
if (
!Object.hasOwn(SORT_COLUMNS, input.sort) ||
!Object.hasOwn(SORT_DIRECTIONS, input.direction)
) {
throw new Error("Unsupported sort option")
}
const sortColumn =
SORT_COLUMNS[input.sort as keyof typeof SORT_COLUMNS]
const sortDirection =
SORT_DIRECTIONS[input.direction as keyof typeof SORT_DIRECTIONS]
const sql = `
SELECT o.id, o.customer_email, o.status, o.total_cents, o.created_at
FROM orders AS o
WHERE o.tenant_id = $1
AND ($2::text IS NULL OR o.customer_email = $2)
AND ($3::text[] IS NULL OR o.status = ANY($3))
ORDER BY ${sortColumn} ${sortDirection}
LIMIT $4
`
return db.query(sql, [
authenticatedTenantId,
input.email,
input.statuses,
input.limit,
])
}La interpolación restante utiliza únicamente valores seleccionados de mapas de constantes tras una comprobación de pertenencia en tiempo de ejecución. Ninguna cadena de la solicitud se convierte en identificador o palabra clave. El inquilino, el correo electrónico, el arreglo de estados y el límite permanecen en la colección de parámetros del controlador.
Valide limit como un entero dentro del rango permitido por el producto antes de la ejecución. Eso protege el uso de recursos y la semántica de la API. Aun así, se vincula como parámetro porque la validación de negocio y la separación entre código y datos cumplen propósitos diferentes.
Para bases de datos sin un parámetro de arreglo conveniente, genere marcadores de posición a partir de la longitud del arreglo y vincule cada elemento:
status IN ($3, $4, $5)
values = [tenantId, email, status1, status2, status3]El texto de los marcadores de posición puede ser generado por código de confianza; los valores de los estados no deben unirse dentro del texto SQL. Defina explícitamente el comportamiento ante un arreglo vacío. Puede significar “sin filtro de estado” o “no coincidir con nada”, y esas decisiones no deben cambiar accidentalmente con el comportamiento del constructor de consultas.
Paso 3: Mantener la gramática dinámica acotada y bajo control del servidor
Los marcadores de posición ordinarios representan valores, no identificadores o palabras clave arbitrarias. Pasar "created_at" como $1 generalmente ordena por un literal de cadena en lugar de seleccionar una columna, y tratar un identificador no confiable con una API de valores no resuelve el requisito del producto.
Exponga un vocabulario público reducido y asígnelo a tokens internos fijos:
createdAt -> o.created_at
total -> o.total_cents
asc -> ASC
desc -> DESCRechace claves desconocidas. No transfiera identificadores entrecomillados, funciones SQL, rutas JSON, intercalaciones, cláusulas de ordenamiento de nulos o expresiones provenientes de la solicitud. Si los requisitos del producto agregan posteriormente un ordenamiento computado, implemente esa expresión en el código del servidor y asígnele una nueva clave pública.
Un helper de entrecomillado de identificadores puede ser apropiado cuando el código administrativo confiable necesita genuinamente un identificador dinámico, pero entrecomillar entradas de usuario arbitrarias a menudo preserva una capacidad más amplia de la que el endpoint debería tener. Para una API normal, un mapeo estricto es más fácil de revisar y probar.
Paso 4: Preservar el aislamiento de inquilinos de forma independiente
Derive authenticatedTenantId a partir de la sesión, token o identidad de servicio verificada. No confíe en un valor de inquilino duplicado en el cuerpo JSON, cadena de consulta (query string) o estado del navegador. Cada lectura y escritura que toque filas pertenecientes a un inquilino debe incluir el alcance autorizado o utilizar una abstracción de repositorio que lo exija.
La matriz de pruebas debe incluir un ID de pedido, correo electrónico o estado válido perteneciente a otro inquilino. La consulta no debe devolver ninguna fila de este tipo, incluso cuando todos los valores SQL sean sintácticamente inofensivos. Esto detecta una falla de autorización que las pruebas de inyección por sí solas no pueden detectar.
La seguridad a nivel de filas (Row-Level Security) de la base de datos puede agregar otro límite cuando se configura alrededor de una identidad de sesión confiable y se prueba a través del pool de conexiones. No justifica la falta de autorización en el servicio, el manejo inseguro de variables de sesión o la concatenación SQL. Trátela como una capa de cumplimiento adicional con su propia configuración y pruebas.
Paso 5: Auditar ORMs, procedimientos almacenados y rutas de segundo orden
La API de filtrado normal de un ORM suele vincular valores, pero los métodos de ejecución directa pueden ofrecer tanto formas parametrizadas seguras como formas de cadena explícitamente inseguras. Revise el método exacto, la versión del framework y la ruta del controlador. Un nombre de método como queryRaw no es evidencia suficiente por sí solo; demuestre si el SQL final y los valores se envían por separado.
Los procedimientos almacenados son seguros solo cuando sus entradas permanecen como parámetros. Un procedimiento que concatena un parámetro en SQL dinámico y lo ejecuta ha trasladado el límite vulnerable a la base de datos. Busque en los cuerpos de los procedimientos ejecuciones dinámicas y revise los privilegios otorgados al rol de la aplicación.
La inyección de segundo orden ocurre cuando texto hostil se almacena como datos ordinarios y luego se reutiliza como gramática SQL. Por ejemplo, una importación puede insertar de forma segura el nombre de un reporte, mientras que un trabajo nocturno de generación de reportes concatena posteriormente ese nombre en una consulta. La primera escritura puede estar parametrizada y aun así dejar la ejecución posterior vulnerable. Aplique parametrización o un mapeo fijo en cada límite de ejecución, independientemente de si el valor provino directamente de una solicitud, otro servicio, un archivo o una fila existente en la base de datos.
Paso 6: Agregar defensa en profundidad sin ocultar el defecto
Utilice un rol de base de datos en tiempo de ejecución solo con las tablas y operaciones requeridas. Un servicio de búsqueda de solo lectura no debería ser propietario del esquema ni tener permisos para eliminar tablas (drop), crear extensiones o actualizar datos no relacionados. Las migraciones y la administración deben usar credenciales independientes. El privilegio mínimo limita el daño si una inyección u otro compromiso de la aplicación persiste; no hace que una consulta insegura sea aceptable.
La validación de entradas debe aplicar tipos de negocio, longitudes, pertenencia a enums y rangos. Puede rechazar solicitudes mal formadas de manera temprana y reducir el abuso. Sigue siendo secundaria porque un valor que pasa la validación aún puede ser peligroso en el contexto SQL incorrecto, y los campos de formato libre como los nombres contienen legítimamente signos de puntuación.
El escape manual es frágil y específico de cada base de datos. No cree un helper general escapeSql ni concatene su salida. Si una ruta heredada no se puede reemplazar de inmediato, aíslela, use la funcionalidad documentada del proveedor de la base de datos, restrinja los privilegios, agregue pruebas y rastree su eliminación. Un firewall de aplicaciones web puede proporcionar detección temporal o parches virtuales durante un incidente, pero no puede probar que cada ruta de ejecución en la base de datos sea segura.
Devuelva un error genérico al cliente y registre un evento interno estructurado con la ruta, operación, despliegue y clase de error de la base de datos. No devuelva texto SQL, trazas de pila (stack traces), detalles de conexión o valores de parámetros confidenciales al emisor. Evite registrar secretos o datos personales completos, conservando suficientes identificadores para investigar.
Paso 7: Verificar la reparación en el límite de la consulta
Construya pruebas de integración contra el controlador y el dialecto de base de datos reales. Incluya cadenas que contengan comillas, marcadores de comentarios, puntos y comas, Unicode, caracteres comodín y texto que se asemeje a SQL. La aserción no es simplemente “la solicitud no falló”. El valor debe tratarse de forma literal, la estructura de la consulta debe permanecer fija y el endpoint debe devolver solo filas autorizadas.
Cubra al menos:
- El texto del correo electrónico con comillas o caracteres similares a comentarios se mantiene como una comparación literal.
- Cada clave de ordenamiento permitida produce la cláusula
ORDER BYfija esperada. - Las claves de ordenamiento, direcciones y estados desconocidos, así como límites fuera de rango, se rechazan antes de consultar.
- Los arreglos vacíos, de un solo elemento y de múltiples elementos tienen un comportamiento definido y utilizan valores vinculados.
- Un emisor no puede recuperar la fila de otro inquilino modificando ningún campo de la solicitud.
- Las rutas de consultas directas de ORM y procedimientos almacenados reciben el mismo corpus hostil.
- Las cadenas almacenadas utilizadas posteriormente por trabajos de reportes o mantenimiento permanecen como datos en el segundo límite de ejecución.
- El rol de base de datos en tiempo de ejecución no puede realizar cambios de esquema ni acceder a tablas no relacionadas.
El análisis estático y la revisión de código pueden evitar nuevas rutas de interpolación sin procesar. Configure una regla o punto de control de revisión para métodos de consulta directa inseguros y construcción de cadenas adyacentes a SQL, pero mantenga las pruebas de integración porque los wrappers, el código generado, los procedimientos y el comportamiento del controlador pueden no ser visibles mediante una simple búsqueda de texto.
Paso 8: Desplegar, observar y responder
Realice el despliegue monitoreando la tasa de errores de la base de datos, la latencia del endpoint, el conteo de entradas rechazadas y la estructura de las consultas. Un aumento repentino en errores de sintaxis, fallas de permisos o claves de ordenamiento rechazadas puede revelar clientes no actualizados o sondeos activos. No registre la carga útil hostil completa simplemente para contabilizarla.
Si se sospecha una inyección real, deshabilite o restrinja el endpoint vulnerable, preserve la evidencia relevante de auditoría y despliegue, rote las credenciales de base de datos expuestas y determine qué pudo haber leído o modificado el rol en tiempo de ejecución. Revise los registros de base de datos y los registros de negocio para detectar accesos no autorizados, repare cada ruta de ejecución equivalente y agregue la clase descubierta al corpus de regresión antes de restaurar el acceso normal.
La finalización significa que la ruta gramatical insegura ha desaparecido, la autorización del inquilino se mantiene obligatoria, el rol en tiempo de ejecución está acotado, todos los ejecutores de consultas fueron inventariados y las pruebas demuestran que las entradas hostiles permanecen como datos tanto en la ejecución inmediata como en la posterior.
Respuesta de muestra de alta calidad
“Primero haría un inventario de cada ruta de ejecución SQL utilizada por el endpoint, incluyendo los métodos directos del ORM y los procedimientos almacenados. La consulta original mezcla cinco preocupaciones diferentes. El inquilino, correo electrónico, valores de estado y límite son datos; la columna y dirección de ordenamiento son gramática SQL; y el alcance del inquilino es también una decisión de autorización.
Derivaría el inquilino a partir del contexto autenticado del servidor y lo vincularía a través del controlador junto con el correo electrónico, el arreglo de estados tipado y el límite entero acotado. Expondría únicamente createdAt y total como claves de ordenamiento y asc y desc como direcciones, mapeándolos luego a tokens SQL constantes tras validaciones de pertenencia en tiempo de ejecución. Los valores desconocidos serían rechazados. Si el controlador carece de vinculación para arreglos, generaría únicamente la lista de marcadores de posición y vincularía cada elemento por separado.
Verificaría la API exacta de consultas directas del ORM en lugar de asumir que todas las llamadas de ORM son seguras. También inspeccionaría los procedimientos almacenados en busca de ejecución dinámica. Los valores almacenados previamente siguen sin ser confiables cuando un reporte o trabajo por lotes construye SQL más adelante, por lo que deben parametrizarse nuevamente en ese límite de ejecución.
Para la defensa en profundidad, el servicio de búsqueda utilizaría un rol de base de datos que solo pueda leer las columnas o vistas requeridas y no pueda alterar el esquema. La validación de negocio aplicaría los enums, longitudes y límites permitidos, mientras que la parametrización sigue siendo la defensa contra inyecciones. Las respuestas al cliente serían genéricas; los registros internos capturarían la operación y la clase de error sin texto SQL ni parámetros confidenciales.
Finalmente, ejecutaría pruebas de integración utilizando el controlador real. Comillas, comentarios, texto con apariencia de SQL, Unicode y elementos de arreglos deben permanecer como valores literales. Las pruebas cubrirían cada opción de ordenamiento permitida y rechazada, listas vacías y grandes, rutas de ORM y procedimientos, reutilización de datos almacenados e identificadores entre inquilinos. La corrección está completa solo cuando la estructura de la consulta se mantiene fija y ninguna solicitud puede devolver filas de otro inquilino”.
Errores comunes
- Parametrizar solo el correo electrónico → el inquilino, los elementos de la lista, el límite u otro filtro aún pueden alterar el SQL → vincular cada valor e inventariar toda la ruta de ejecución.
- Vincular el nombre de columna solicitado como un valor normal → un marcador de posición de valor no representa un identificador → mapear un enum público reducido a tokens SQL fijos gestionados por el servidor.
- Entrecomillar cualquier identificador solicitado → el endpoint sigue otorgando a los emisores control sobre una amplia superficie gramatical → exponer solo las claves de ordenamiento requeridas por el producto y rechazar el resto.
- Unir una lista de estados validada dentro de
IN (...)→ la validación puede desincronizarse y cada elemento reingresa al texto SQL → usar un parámetro de arreglo tipado o generar marcadores de posición y vincular cada elemento. - Tomar
tenantIdde la solicitud porque está parametrizado → la separación de código y datos no autoriza al inquilino seleccionado → derivar el alcance del inquilino a partir del contexto autenticado del servidor. - Asumir que el ORM previene toda inyección → los métodos directos o inseguros pueden eludir la vinculación normal → verificar la API exacta y la llamada final al controlador.
- Trasladar la construcción de cadenas a un procedimiento almacenado → el SQL dinámico dentro del procedimiento sigue siendo inyectable → mantener los parámetros de entrada del procedimiento parametrizados hasta la ejecución final.
- Sanitizar solo en la escritura original → el texto hostil almacenado puede convertirse más tarde en gramática SQL en un trabajo de reportes → proteger cada límite de ejecución contra la inyección de segundo orden.
- Escapar comillas con un helper personalizado → las diferencias de dialecto, codificación y contexto hacen que el escape manual sea frágil → usar la interfaz de vinculación de parámetros del lado del servidor del controlador.
- Otorgar privilegios de propietario a la aplicación porque la consulta está corregida → otro defecto o filtración de credenciales tendrá un radio de impacto innecesario → usar un rol de tiempo de ejecución con privilegios mínimos y separar las credenciales de migración.
- Devolver el error de base de datos y el SQL para depuración → los emisores obtienen detalles del esquema y de la consulta, mientras que los registros pueden exponer datos personales → devolver un error genérico y registrar evidencia interna estructurada y redactada.
- Probar una sola carga útil famosa y detenerse → la autorización, los arreglos, los procedimientos almacenados, las rutas directas alternativas y la ejecución de segundo orden quedan sin probar → verificar la estructura de la consulta y los resultados de inquilinos en un corpus variado.
Preguntas de seguimiento
Pregunta de seguimiento 1: ¿Pueden las sentencias preparadas proteger nombres dinámicos de tablas o columnas?
Los parámetros de vinculación ordinarios representan valores. Generalmente no reemplazan nombres de tablas, nombres de columnas, operadores o palabras clave de SQL. Rediseñe la API para que los emisores elijan entre un enum reducido, y luego asigne cada clave permitida a un fragmento SQL fijo propiedad de la aplicación. Si las herramientas administrativas confiables realmente necesitan identificadores dinámicos, use la funcionalidad de identificadores del proveedor de la base de datos y un límite de autorización mucho más estricto; no exponga esa capacidad a través de un endpoint de búsqueda regular orientado al usuario.
Pregunta de seguimiento 2: ¿Es suficiente un ORM para prevenir la inyección SQL?
Solo si la API seleccionada preserva la vinculación de parámetros hasta la llamada final al controlador. Los métodos normales de igualdad y filtrado a menudo lo hacen. Los métodos de cadenas directas, variantes inseguras, nombres de campo dinámicos, operadores personalizados y extensiones podrían no hacerlo. Revise la versión desplegada del framework, inspeccione el SQL y los valores generados en un entorno de prueba seguro y ejecute pruebas de integración con entradas hostiles. El ORM reduce las oportunidades de cometer errores; no elimina la necesidad de comprender sus vías de escape.
Pregunta de seguimiento 3: ¿Qué es la inyección SQL de segundo orden?
El valor controlado por el atacante se almacena primero como datos sin alterar la sentencia original. Un proceso posterior lee ese valor y lo concatena en SQL dinámico, donde altera la gramática de la nueva sentencia. La reparación corresponde al límite de ejecución posterior: vincule el valor almacenado como datos o asígnelo a un token fijo si se supone que debe seleccionar gramática. Trate las filas de la base de datos, archivos, colas y servicios internos como fuentes no confiables cuando influyan en el código SQL ejecutable.
Pregunta de seguimiento 4: ¿Debería la aplicación rechazar cada comilla, punto y coma o palabra clave SQL?
No. Los nombres, textos de búsqueda y otros datos legítimos pueden contener signos de puntuación o palabras que se asemejen a SQL. La validación de negocio debe aplicar el tipo, longitud, formato, enum y rango reales del dominio. La parametrización proporciona la propiedad de seguridad al hacer que el valor completo sea tratado como datos literales. Una lista de denegación (denylist) es incompleta y también puede romper entradas válidas.
Pregunta de seguimiento 5: ¿Cómo se maneja una lista dinámica grande para IN?
Utilice el arreglo tipado o el parámetro con valores de tabla de la base de datos cuando esté disponible, o genere un marcador de posición por elemento y vincule cada valor. Defina un tamaño máximo de lista para el control de recursos. Las listas muy grandes pueden justificar una tabla temporal, un mecanismo de carga masiva (bulk-load) o una API diferente, pero los valores insertados siguen utilizando datos vinculados o datos de protocolo masivo en lugar de concatenación de cadenas SQL.
Pregunta de seguimiento 6: ¿Qué haría durante un incidente confirmado de inyección en producción?
Restrinja la ruta vulnerable, preserve la evidencia de auditoría del despliegue, base de datos y aplicación, y rote las credenciales que puedan haberse expuesto. Use los permisos del rol de tiempo de ejecución para delimitar la investigación, compruebe lecturas y escrituras no autorizadas y repare todos los constructores de consultas y procedimientos equivalentes. Agregue la ruta observada a las pruebas de regresión, reduzca privilegios donde sea posible y restablezca el tráfico solo después de que la consulta corregida y el límite del inquilino estén verificados.