SQL Injection
What it is
Unsanitized input reaches a SQL query and changes its logic — from a tautology that returns extra rows to blind, stacked queries that exfiltrate data one character at a time.
Why it matters
Still one of the highest-severity classes available: a single injectable parameter can mean full database read/write, and occasionally remote code execution through database-server features.
Where to look
- Any parameter feeding a lookup, filter, sort or search — including ones not visible in the UI
- Hidden POST fields, JSON body keys, HTTP headers (
,User-Agent
), and occasionally parameter names themselvesX-Forwarded-For
How I test it
- Find a parameter that plausibly touches a database — search, filter, sort, an ID lookup.
- Send a single quote and see if it breaks the response: an error message, a blank page, or a different status code.
- Try a boolean pair —
vs... AND 1=1
— and diff the two responses.... AND 1=2 - No visible difference? Move to time-based blind: an equivalent of
/WAITFOR DELAY
and measure response time.SLEEP() - Once confirmed, map the injection type — error-based, union, boolean-blind, time-blind — and extract schema info (table/column names) before touching actual user rows.
Hunting checklist
- Tested a single quote for a syntax break
- Tested a boolean true/false pair and diffed responses
- Tested time-based blind if no visible difference appeared
- Tested hidden fields and headers, not just visible query params
- Confirmed injection type before extracting any real data
- Stopped at schema-level proof, didn't dump user tables unnecessarily
Evidence to collect
- Baseline request/response
- Request with the boolean-true payload and its response
- Request with the boolean-false payload and its response
- Timing proof for blind cases
- Minimal schema-level data proving impact (e.g. database version), not a full data dump
Recommended fix
- Parameterized queries / prepared statements everywhere
- Least-privilege database accounts
- Input validation as defense in depth, never as the only control
- Generic error messages — don't leak query structure
Qué es
Un input sin sanear llega a una consulta SQL y altera su lógica — desde una tautología que devuelve filas de más, hasta consultas ciegas y apiladas que exfiltran datos carácter a carácter.
Por qué importa
Sigue siendo una de las categorías de más severidad disponibles: un solo parámetro inyectable puede significar lectura/escritura completa de la base de datos, y en ocasiones ejecución remota de código a través de funcionalidades del propio motor.
Dónde buscar
- Cualquier parámetro que alimente una búsqueda, filtro, orden o lookup — incluyendo los que no se ven en la UI
- Campos POST ocultos, claves del JSON, cabeceras HTTP (
,User-Agent
), y en ocasiones los propios nombres de parámetroX-Forwarded-For
Cómo lo pruebo
- Encuentra un parámetro que plausiblemente toque una base de datos — búsqueda, filtro, orden, lookup por ID.
- Envía una comilla simple y comprueba si rompe la respuesta: mensaje de error, página en blanco, o un código de estado distinto.
- Prueba un par booleano —
frente a... AND 1=1
— y compara las dos respuestas.... AND 1=2 - ¿Sin diferencia visible? Pasa a ciego basado en tiempo: un equivalente de
/WAITFOR DELAY
y mide el tiempo de respuesta.SLEEP() - Una vez confirmado, mapea el tipo de inyección — basada en error, union, ciega booleana, ciega por tiempo — y extrae información de schema (nombres de tabla/columna) antes de tocar filas de usuarios reales.
Checklist de hunting
- Probada una comilla simple buscando romper la sintaxis
- Probado un par booleano verdadero/falso y comparadas las respuestas
- Probado ciego por tiempo si no hubo diferencia visible
- Probados campos ocultos y cabeceras, no solo parámetros visibles de la query
- Confirmado el tipo de inyección antes de extraer datos reales
- Parado en la prueba a nivel de schema, sin volcar tablas de usuarios innecesariamente
Evidencias para el reporte
- Petición/respuesta de referencia
- Petición con el payload booleano-verdadero y su respuesta
- Petición con el payload booleano-falso y su respuesta
- Prueba de tiempos para los casos ciegos
- Dato mínimo a nivel de schema que demuestre el impacto (p. ej. versión de la base de datos), no un volcado completo
Mitigación
- Consultas parametrizadas / prepared statements en todas partes
- Cuentas de base de datos con mínimo privilegio
- Validación de input como defensa en profundidad, nunca como único control
- Mensajes de error genéricos — no filtrar la estructura de la query