flawopen.com/SQL Injection/¿Basta con escapar?
¿Escapar comillas evita SQL Injection?
Por lo general, no. Una función de escape depende de la base de datos, el juego de caracteres de la conexión, el modo SQL y el contexto exacto de las comillas. Las consultas parametrizadas mantienen los valores fuera de la sintaxis SQL mediante la API del controlador. Usa parámetros para los valores; para identificadores dinámicos que no admiten parámetros, elige una opción de una lista permitida por la aplicación.
Por qué el escape es frágil en la práctica
El escape no es una sustitución de texto independiente del contexto. MySQL documenta que mysql_real_escape_string() usa el juego de caracteres de la conexión y que SET NAMES no actualiza el estado de la biblioteca cliente que utiliza esa función. Una discrepancia puede invalidar las suposiciones sobre el texto escapado. Prefiere APIs parametrizadas; si el código heredado aún necesita escape, usa la API del controlador con el juego de caracteres configurado mediante su función compatible y gestiona los errores.
El escape debe aplicarse correctamente en cada punto de interpolación, con el controlador, juego de caracteres, modo SQL y contexto de literal adecuados. Las APIs parametrizadas hacen explícita la separación entre valor y dato en la llamada y evitan construir manualmente una cadena entre comillas.
Escapar comillas solo trata caracteres dentro de un literal de cadena concreto. No construye de forma segura una columna, tabla, palabra clave o dirección de ordenación, ni protege entradas en expresiones numéricas sin comillas. Vincula valores y asigna la estructura dinámica a opciones fijas controladas por la aplicación.
// La entrada del usuario se interpola en la sintaxis SQL
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
// El controlador vincula name como valor
cursor.execute(
"SELECT id FROM users WHERE name = ?",
(name,),
)
Qué cambia con la vinculación de parámetros
Una consulta parametrizada pasa la estructura SQL y los valores como entradas separadas mediante la API de la base de datos. Los controladores pueden usar sentencias preparadas nativas u otra implementación segura; no presupongas una secuencia concreta del protocolo. La propiedad de seguridad es que el valor se trate como dato y no se vuelva a analizar como sintaxis SQL. En Python DB-API, usa el marcador del controlador y pasa los valores por separado; no construyas la consulta con formato de cadenas.
Lista de verificación de ingeniería
- ✓Vincula cada valor de usuario mediante la API del controlador; nunca lo formatees dentro del texto SQL.
- ✓Asigna nombres dinámicos de tablas, columnas y ordenación a listas permitidas fijas de la aplicación.
- ✓Si aún no puedes retirar el escape heredado, usa la API del controlador adecuada al contexto y verifica el juego de caracteres y el modo SQL de la conexión.
- ✓Prueba la construcción de consultas con comillas, texto no ASCII e identificadores inválidos sin enviar sentencias destructivas.
Preguntas frecuentes
Para identificadores o fragmentos SQL que no admiten parámetros, usa solo una API de cita compatible con el controlador y una lista permitida fija de la aplicación. No uses escape genérico para valores arbitrarios.
Una función mantenida puede ayudar en su contexto documentado, pero depende de la base de datos, la configuración de conexión, el contexto de cita y el uso correcto en cada punto. Las consultas parametrizadas son la opción predeterminada más segura para valores.
Referencias
- OWASP — guía para prevenir SQL Injection
- CWE-89 — SQL Injection
- Manual de PHP — mysqli::real_escape_string
- API C de MySQL — mysql_real_escape_string
- flawopen.com — SQL Injection (todos los idiomas)