Use declaraciones preparadas y consultas parametrizadas. Estas son declaraciones SQL que el servidor de la base de datos envía y analiza por separado de cualquier parámetro. De esta manera, es imposible que un atacante inyecte SQL malicioso.
Básicamente tienes dos opciones para lograr esto:
Usando PDO (para cualquier controlador de base de datos compatible):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
Usando MySQLi (para MySQL):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
Si se está conectando a una base de datos que no sea MySQL, hay una segunda opción específica del controlador a la que puede referirse (por ejemplo, pg_prepare()
y pg_execute()
para PostgreSQL). PDO es la opción universal.
Configurar correctamente la conexión
Tenga en cuenta que cuando se usa PDO
para acceder a una base de datos MySQL, las declaraciones preparadas reales no se usan por defecto . Para solucionar esto, debe deshabilitar la emulación de las declaraciones preparadas. Un ejemplo de crear una conexión usando PDO es:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
En el ejemplo anterior, el modo de error no es estrictamente necesario, pero se recomienda agregarlo . De esta manera, el script no se detendrá con un Fatal Error
cuando algo sale mal. Y le da al desarrollador la oportunidad de catch
cualquier error que sea throw
n como PDOException
s.
Sin embargo, lo que es obligatorio es la primera setAttribute()
línea, que le dice a PDO que deshabilite las declaraciones preparadas emuladas y use real declaraciones preparadas . Esto asegura que la declaración y los valores no sean analizados por PHP antes de enviarlos al servidor MySQL (dando a un posible atacante la posibilidad de inyectar SQL malicioso).
Aunque puede establecer las charset
opciones en el constructor, es importante tener en cuenta que las versiones 'antiguas' de PHP (anteriores a 5.3.6) ignoraron en silencio el parámetro charset en el DSN.
Explicación
El prepare
servidor de bases de datos analiza y compila la instrucción SQL a la que pasa . Al especificar parámetros (ya sea uno ?
o un parámetro con nombre como :name
en el ejemplo anterior), le dice al motor de la base de datos dónde desea filtrar. Entonces cuando llamasexecute
, la declaración preparada se combina con los valores de los parámetros que especifique.
Lo importante aquí es que los valores de los parámetros se combinan con la declaración compilada, no con una cadena SQL. La inyección de SQL funciona engañando al script para que incluya cadenas maliciosas cuando crea SQL para enviar a la base de datos. Entonces, al enviar el SQL real por separado de los parámetros, limita el riesgo de terminar con algo que no pretendía.
Cualquier parámetro que envíe cuando use una declaración preparada solo se tratará como cadenas (aunque el motor de la base de datos puede hacer alguna optimización, por lo que los parámetros también pueden terminar como números, por supuesto). En el ejemplo anterior, si la $name
variable contiene 'Sarah'; DELETE FROM employees
el resultado, simplemente sería una búsqueda de la cadena "'Sarah'; DELETE FROM employees"
, y no terminará con una tabla vacía .
Otro beneficio de usar declaraciones preparadas es que si ejecuta la misma declaración muchas veces en la misma sesión, solo se analizará y compilará una vez, lo que le dará algunas ganancias de velocidad.
Ah, y dado que preguntaste cómo hacerlo para un inserto, aquí hay un ejemplo (usando PDO):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
¿Se pueden usar declaraciones preparadas para consultas dinámicas?
Si bien aún puede usar declaraciones preparadas para los parámetros de consulta, la estructura de la consulta dinámica en sí no se puede parametrizar y ciertas características de consulta no se pueden parametrizar.
Para estos escenarios específicos, lo mejor que puede hacer es usar un filtro de lista blanca que restrinja los valores posibles.
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
Si está utilizando una versión reciente de PHP, la
mysql_real_escape_string
opción descrita a continuación ya no estará disponible (aunquemysqli::escape_string
es un equivalente moderno). En estos días, lamysql_real_escape_string
opción solo tendría sentido para el código heredado en una versión anterior de PHP.Tienes dos opciones: escapar de los caracteres especiales en tu
unsafe_variable
, o usar una consulta parametrizada. Ambos lo protegerían de la inyección SQL. La consulta parametrizada se considera la mejor práctica, pero requerirá cambiar a una extensión MySQL más nueva en PHP antes de poder usarla.Cubriremos la cuerda de menor impacto que escapa uno primero.
Véanse también los detalles de
mysql_real_escape_string
función.Para usar la consulta parametrizada, debe usar MySQLi en lugar de las funciones de MySQL . Para reescribir su ejemplo, necesitaríamos algo como lo siguiente.
La función clave que querrá leer allí sería
mysqli::prepare
.Además, como han sugerido otros, puede resultarle útil / más fácil intensificar una capa de abstracción con algo como PDO .
Tenga en cuenta que el caso sobre el que preguntó es bastante simple y que los casos más complejos pueden requerir enfoques más complejos. En particular:
mysql_real_escape_string
. En este tipo de caso, sería mejor que pasara la entrada del usuario a través de una lista blanca para asegurarse de que solo se permitan valores "seguros".mysql_real_escape_string
enfoque, sufrirá el problema descrito por Polynomial en los comentarios a continuación. Este caso es más complicado porque los enteros no estarían rodeados de comillas, por lo que podría lidiar al validar que la entrada del usuario contiene solo dígitos.fuente
mysql_real_escape_string
es suficiente o debo usar parametrizado también?htmlentities
por ejemplomysql_real_escape_string()
para completar, pero no soy fanático de enumerar primero el enfoque más propenso a errores. El lector podría tomar rápidamente el primer ejemplo. Menos mal que ahora está en desuso :)mysql_*
funciones están en desuso. Fueron reemplazados por funciones similaresmysqli_*
, comomysqli_real_escape_string
.Cada respuesta aquí cubre solo una parte del problema. De hecho, hay cuatro partes de consulta diferentes que podemos agregar dinámicamente a SQL:
Y las declaraciones preparadas cubren solo dos de ellas.
Pero a veces tenemos que hacer nuestra consulta aún más dinámica, agregando operadores o identificadores también. Por lo tanto, necesitaremos diferentes técnicas de protección.
En general, este enfoque de protección se basa en la lista blanca .
En este caso, cada parámetro dinámico debe estar codificado en su script y elegido de ese conjunto. Por ejemplo, para hacer un pedido dinámico:
Para facilitar el proceso, escribí una función auxiliar de la lista blanca que hace todo el trabajo en una línea:
Hay otra forma de proteger los identificadores: escapar, pero prefiero seguir en la lista blanca como un enfoque más sólido y explícito. Sin embargo, siempre que tenga un identificador entre comillas, puede escapar del carácter de comillas para que sea seguro. Por ejemplo, de forma predeterminada para mysql, debe duplicar el carácter de comillas para escapar de él . Para otros otros DBMS, las reglas de escape serían diferentes.
Aún así, hay un problema con las palabras clave de sintaxis SQL (como
AND
,DESC
y tal), pero la lista blanca parece ser el único enfoque en este caso.Entonces, una recomendación general puede expresarse como
Actualizar
Aunque hay un acuerdo general sobre las mejores prácticas con respecto a la protección de inyección SQL, todavía hay muchas malas prácticas también. Y algunos de ellos están demasiado arraigados en la mente de los usuarios de PHP. Por ejemplo, en esta misma página hay (aunque invisibles para la mayoría de los visitantes) más de 80 respuestas eliminadas , todas eliminadas por la comunidad debido a la mala calidad o la promoción de prácticas malas y obsoletas. Peor aún, algunas de las malas respuestas no se eliminan, sino que prosperan.
Por ejemplo, hay (1) (2) todavía (3) muchas (4) respuestas (5) , incluida la segunda respuesta más votada sugiere el escape manual de cadenas: un enfoque obsoleto que se ha demostrado que es inseguro.
O hay una respuesta un poco mejor que sugiere otro método de formato de cadena e incluso se jacta de ser la panacea definitiva. Si bien, por supuesto, no lo es. Este método no es mejor que el formato de cadena normal, pero mantiene todos sus inconvenientes: es aplicable solo a cadenas y, como cualquier otro formato manual, es esencialmente una medida opcional, no obligatoria, propensa a errores humanos de cualquier tipo.
Creo que todo esto se debe a una superstición muy antigua, respaldada por autoridades como OWASP o el manual de PHP. , que proclama la igualdad entre cualquier "escape" y protección contra las inyecciones SQL.
Independientemente de lo que el manual de PHP haya dicho durante años,
*_escape_string
de ninguna manera hace que los datos sean seguros y nunca ha sido pensado. Además de ser inútil para cualquier parte de SQL que no sea cadena, el escape manual es incorrecto, porque es manual en lugar de automatizado.Y OWASP lo empeora aún más, haciendo hincapié en escapar de la entrada del usuario, lo cual es una absoluta tontería: no debería haber tales palabras en el contexto de la protección de inyección. Cada variable es potencialmente peligrosa, ¡sin importar la fuente! O, en otras palabras, cada variable tiene que formatearse adecuadamente para ser puesta en una consulta, sin importar la fuente nuevamente. Es el destino lo que importa. En el momento en que un desarrollador comienza a separar las ovejas de las cabras (pensando si alguna variable particular es "segura" o no), él / ella da su primer paso hacia el desastre. Sin mencionar que incluso la redacción sugiere un escape masivo en el punto de entrada, que se asemeja a la característica de citas muy mágicas, ya despreciadas, desaprobadas y eliminadas.
Entonces, a diferencia de cualquier "escape", las declaraciones preparadas son la medida que realmente protege de la inyección SQL (cuando corresponde).
fuente
Recomiendo usar PDO (PHP Data Objects) para ejecutar consultas SQL parametrizadas.
Esto no solo protege contra la inyección de SQL, sino que también acelera las consultas.
Y al usar PDO en lugar de
mysql_
,mysqli_
ypgsql_
funciones, hace que su aplicación sea un poco más abstracta de la base de datos, en el raro caso de que tenga que cambiar de proveedor de base de datos.fuente
Uso
PDO
y consultas preparadas.(
$conn
es unPDO
objeto)fuente
Como puede ver, las personas sugieren que utilice declaraciones preparadas como máximo. No está mal, pero cuando su consulta se ejecuta solo una vez por proceso, habrá una ligera penalización de rendimiento.
Estaba enfrentando este problema, pero creo que lo resolví de una manera muy sofisticada, la forma en que los hackers usan para evitar el uso de comillas. Usé esto junto con declaraciones preparadas emuladas. Lo uso para prevenir todo tipo de posibles ataques de inyección SQL.
Mi acercamiento:
Si espera que la entrada sea entera, asegúrese de que sea realmente entera. En un lenguaje de tipo variable como PHP, esto es muy importante. Puede usar, por ejemplo, esta solución muy simple pero poderosa:
sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
Si espera algo más de un número hexadecimal hechizo . Si lo hechizas, escaparás perfectamente de toda entrada. En C / C ++ hay una función llamada
mysql_hex_string()
, en PHP puedes usarbin2hex()
.No se preocupe por que la cadena de escape tendrá un tamaño 2x de su longitud original porque incluso si la usa
mysql_real_escape_string
, PHP tiene que asignar la misma capacidad((2*input_length)+1)
, que es la misma.Este método hexadecimal se usa a menudo cuando transfiere datos binarios, pero no veo ninguna razón por la que no lo use en todos los datos para evitar ataques de inyección SQL. Tenga en cuenta que debe anteponer los datos
0x
o utilizar la función MySQL en suUNHEX
lugar.Entonces, por ejemplo, la consulta:
Se convertirá:
o
Hex es el escape perfecto. No hay forma de inyectar.
Diferencia entre la función UNHEX y el prefijo 0x
Hubo cierta discusión en los comentarios, así que finalmente quiero dejarlo claro. Estos dos enfoques son muy similares, pero son un poco diferentes en algunos aspectos:
El ** ** 0x prefijo sólo se puede utilizar para las columnas de datos como char, varchar, texto, bloque, binario, etc .
Además, su uso es un poco complicado si está a punto de insertar una cadena vacía. Tendrá que reemplazarlo por completo
''
o recibirá un error.UNHEX () funciona en cualquier columna; No tiene que preocuparse por la cadena vacía.
Los métodos hexadecimales se usan a menudo como ataques
Tenga en cuenta que este método hexadecimal se usa a menudo como un ataque de inyección SQL donde los enteros son como cadenas y se escapan solo con
mysql_real_escape_string
. Entonces puede evitar el uso de comillas.Por ejemplo, si solo haces algo como esto:
Un ataque puede inyectarte muy fácilmente . Considere el siguiente código inyectado devuelto por su script:
y ahora solo extrae la estructura de la tabla:
Y luego simplemente seleccione los datos que desee. ¿No es genial?
Pero si el codificador de un sitio inyectable lo hechizara, no sería posible ninguna inyección porque la consulta se vería así:
SELECT ... WHERE id = UNHEX('2d312075...3635')
fuente
+
pero conCONCAT
. Y para el rendimiento: no creo que afecte el rendimiento porque mysql tiene que analizar los datos y no importa si el origen es cadena o hexadecimal'root'
o puedes hexadecimal0x726f6f74
PERO si quieres un número y enviarlo como una cadena probablemente escribirás '42' no CHAR (42 ) ... '42' en hexadecimal sería0x3432
no0x42
SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ')
Prevención de inyección - mysql_real_escape_string ()
PHP tiene una función especialmente diseñada para prevenir estos ataques. Todo lo que necesitas hacer es utilizar la función de un bocado
mysql_real_escape_string
.mysql_real_escape_string
toma una cadena que se va a usar en una consulta MySQL y devuelve la misma cadena con todos los intentos de inyección SQL escapados de forma segura. Básicamente, reemplazará esas comillas problemáticas (') que un usuario podría ingresar con un sustituto seguro de MySQL, una comilla escapada'.NOTA: ¡debe estar conectado a la base de datos para usar esta función!
// Conéctate a MySQL
Puede encontrar más detalles en MySQL - Prevención de inyección SQL .
fuente
mysql_real_escape_string
propósito es permitir construir una consulta SQL correcta para cada cadena de datos de entrada. La inyección sql de prevención es el efecto secundario de esta función.mysql_real_escape_string()
No es infalible .mysql_real_escape_string
ahora está en desuso, por lo que ya no es una opción viable. Será eliminado en el futuro de PHP. Es mejor pasar a lo que recomiendan las personas de PHP o MySQL.Podrías hacer algo básico como esto:
Esto no resolverá todos los problemas, pero es un muy buen trampolín. Omití elementos obvios como verificar la existencia, el formato de la variable (números, letras, etc.).
fuente
$q = "SELECT col FROM tbl WHERE x = $safe_var";
por ejemplo. Establecer$safe_var
en1 UNION SELECT password FROM users
trabajos en este caso debido a la falta de comillas. También es posible inyectar cadenas en la consulta usandoCONCAT
yCHR
.mysql_real_escape_string()
No es infalible .mysql_real_escape_string
ahora está en desuso, por lo que ya no es una opción viable. Será eliminado en el futuro de PHP. Es mejor pasar a lo que recomiendan las personas de PHP o MySQL.Lo que sea que termines usando, asegúrate de verificar que tu entrada no haya sido destrozada por
magic_quotes
alguna otra basura bien intencionada, y si es necesario, revísalostripslashes
o lo que sea para desinfectarlo.fuente
La consulta parametrizada Y la validación de entrada es el camino a seguir. Hay muchos escenarios bajo los cuales puede ocurrir la inyección SQL, aunque
mysql_real_escape_string()
se haya utilizado.Esos ejemplos son vulnerables a la inyección de SQL:
o
En ambos casos, no puede usar
'
para proteger la encapsulación.Fuente : La inyección SQL inesperada (cuando escapar no es suficiente)
fuente
En mi opinión, la mejor manera de evitar generalmente la inyección de SQL en su aplicación PHP (o en cualquier aplicación web) es pensar en la arquitectura de su aplicación. Si la única forma de protegerse contra la inyección SQL es recordar usar un método o función especial que haga lo correcto cada vez que hable con la base de datos, lo está haciendo mal. De esa manera, es solo cuestión de tiempo hasta que olvides formatear correctamente tu consulta en algún punto de tu código.
Adoptar el patrón MVC y un marco como CakePHP o CodeIgniter es probablemente el camino correcto: las tareas comunes como la creación de consultas seguras en la base de datos se han resuelto e implementado centralmente en dichos marcos. Le ayudan a organizar su aplicación web de una manera sensata y le hacen pensar más en cargar y guardar objetos que en construir de forma segura consultas SQL únicas.
fuente
Estoy a favor de los procedimientos almacenados ( MySQL ha tenido soporte de procedimientos almacenados desde 5.0 ) desde un punto de vista de seguridad, las ventajas son:
Las desventajas son:
fuente
Hay muchas formas de prevenir las inyecciones SQL y otros hacks SQL. Puede encontrarlo fácilmente en Internet (Búsqueda de Google). Por supuesto, PDO es una de las buenas soluciones. Pero me gustaría sugerirle algunos buenos enlaces de prevención de la inyección SQL.
¿Qué es la inyección SQL y cómo prevenirla?
Manual PHP para inyección SQL
Explicación de Microsoft de la inyección y prevención de SQL en PHP
Y algunos otros como Prevención de inyección SQL con MySQL y PHP .
Ahora, ¿por qué necesita evitar que su consulta se inyecte SQL?
Me gustaría hacerle saber: ¿Por qué tratamos de prevenir la inyección de SQL con un breve ejemplo a continuación:
Consulta para la coincidencia de autenticación de inicio de sesión:
Ahora, si alguien (un hacker) pone
y contraseña cualquier cosa ....
La consulta se analizará en el sistema solo hasta:
La otra parte será descartada. Entonces, ¿qué pasará? Un usuario no autorizado (hacker) podrá iniciar sesión como administrador sin tener su contraseña. Ahora, él / ella puede hacer cualquier cosa que el administrador / persona de correo electrónico pueda hacer. Mira, es muy peligroso si no se previene la inyección SQL.
fuente
Creo que si alguien quiere usar PHP y MySQL o algún otro servidor de base de datos:
(int)$foo
. Lea más sobre el tipo de variables en PHP aquí . Si está utilizando bibliotecas como PDO o MySQLi, use siempre PDO :: quote () y mysqli_real_escape_string () .Ejemplos de bibliotecas:
---- PDO
--- MySQLi
PD :
PDO gana esta batalla con facilidad. Con soporte para doce controladores de bases de datos diferentes y parámetros con nombre, podemos ignorar la pequeña pérdida de rendimiento y acostumbrarnos a su API. Desde el punto de vista de la seguridad, ambos son seguros siempre que el desarrollador los use de la forma en que se supone que deben usarse.
Pero aunque tanto PDO como MySQLi son bastante rápidos, MySQLi se desempeña de manera insignificantemente más rápida en los puntos de referencia: ~ 2.5% para las declaraciones no preparadas y ~ 6.5% para las preparadas.
Y pruebe cada consulta en su base de datos: es una mejor manera de evitar la inyección.
fuente
Si es posible, eche los tipos de sus parámetros. Pero solo funciona en tipos simples como int, bool y float.
fuente
Si desea aprovechar los motores de caché, como Redis o Memcached , quizás DALMP podría ser una opción. Utiliza MySQLi puro . Verifique esto: Capa de abstracción de base de datos DALMP para MySQL usando PHP.
Además, puede 'preparar' sus argumentos antes de preparar su consulta para poder construir consultas dinámicas y al final tener una consulta de declaraciones completamente preparada. Capa de abstracción de base de datos DALMP para MySQL usando PHP.
fuente
Para aquellos que no están seguros de cómo usar PDO (proveniente de las
mysql_
funciones), hice un contenedor PDO muy, muy simple que es un solo archivo. Existe para mostrar lo fácil que es hacer todas las cosas comunes que deben hacerse las aplicaciones. Funciona con PostgreSQL, MySQL y SQLite.Básicamente, léalo mientras lee el manual para ver cómo usar las funciones PDO en la vida real para que sea más fácil almacenar y recuperar valores en el formato que desee.
fuente
Usando esta función PHP
mysql_escape_string()
puede obtener una buena prevención de una manera rápida.Por ejemplo:
mysql_escape_string
- Se escapa de una cadena para usar en un mysql_queryPara más prevención, puede agregar al final ...
Finalmente obtienes:
fuente
Algunas pautas para escapar caracteres especiales en sentencias SQL.
No uses MySQL . Esta extensión está en desuso. Utilice MySQLi o PDO en su lugar.
MySQLi
Para escapar manualmente caracteres especiales en una cadena, puede usar la función mysqli_real_escape_string . La función no funcionará correctamente a menos que el conjunto de caracteres correcto esté configurado con mysqli_set_charset .
Ejemplo:
Para el escape automático de valores con declaraciones preparadas, use mysqli_prepare y mysqli_stmt_bind_param donde se deben proporcionar los tipos para las variables de enlace correspondientes para una conversión adecuada:
Ejemplo:
No importa si usa declaraciones preparadas o
mysqli_real_escape_string
, siempre debe saber el tipo de datos de entrada con los que está trabajando.Entonces, si usa una declaración preparada, debe especificar los tipos de variables para
mysqli_stmt_bind_param
función.Y el uso de
mysqli_real_escape_string
es para, como su nombre lo indica, escapar caracteres especiales en una cadena, por lo que no hará que los enteros sean seguros. El propósito de esta función es evitar romper las cadenas en las declaraciones SQL y el daño a la base de datos que podría causar.mysqli_real_escape_string
es una función útil cuando se usa correctamente, especialmente cuando se combina consprintf
.Ejemplo:
fuente
La alternativa simple a este problema podría resolverse otorgando los permisos apropiados en la base de datos. Por ejemplo: si está utilizando una base de datos MySQL, ingrese a la base de datos a través del terminal o la interfaz de usuario proporcionada y simplemente siga este comando:
Esto restringirá que el usuario se limite solo a la consulta especificada. Elimine el permiso de eliminación para que los datos nunca se eliminen de la consulta activada desde la página PHP. Lo segundo que debe hacer es eliminar los privilegios para que MySQL actualice los permisos y las actualizaciones.
Más información sobre flush .
Para ver los privilegios actuales para el usuario, active la siguiente consulta.
Obtenga más información sobre GRANT .
fuente
Con respecto a muchas respuestas útiles, espero agregar algo de valor a este hilo.
La inyección SQL es un ataque que se puede realizar a través de entradas de usuario (entradas que rellena un usuario y luego se utilizan dentro de las consultas). Los patrones de inyección SQL son una sintaxis de consulta correcta, mientras que podemos llamarla: consultas malas por razones malas, y suponemos que podría haber una persona mala que intenta obtener información secreta (sin pasar por el control de acceso) que afecta los tres principios de seguridad (confidencialidad , integridad y disponibilidad).
Ahora, nuestro objetivo es evitar las amenazas de seguridad, como los ataques de inyección SQL, la pregunta que se hace (cómo evitar un ataque de inyección SQL usando PHP), ser más realista, el filtrado de datos o la eliminación de datos de entrada es el caso cuando se usan datos de entrada del usuario dentro tal consulta, el uso de PHP o cualquier otro lenguaje de programación no es el caso, o según lo recomendado por más personas para usar tecnología moderna como la declaración preparada o cualquier otra herramienta que actualmente soporte la prevención de inyección SQL, ¿considera que estas herramientas ya no están disponibles? ¿Cómo protege su aplicación?
Mi enfoque contra la inyección SQL es: borrar los datos de entrada del usuario antes de enviarlos a la base de datos (antes de usarlos dentro de cualquier consulta).
Filtrado de datos para (convertir datos inseguros en datos seguros)
Tenga en cuenta que PDO y MySQLi no están disponibles. ¿Cómo puede asegurar su aplicación? ¿Me obligas a usarlos? ¿Qué pasa con otros lenguajes que no sean PHP? Prefiero proporcionar ideas generales, ya que puede usarse para un borde más amplio, no solo para un idioma específico.
REGLA: no cree un usuario de base de datos para todos los privilegios. Para todas las operaciones SQL, puede crear su esquema como (deluser, selectuser, updateuser) como nombres de usuario para facilitar su uso.
Ver principio de mínimo privilegio .
Filtrado de datos: antes de generar cualquier consulta del usuario, debe validarse y filtrarse. Para los programadores, es importante definir algunas propiedades para cada variable de entrada del usuario: tipo de datos, patrón de datos y longitud de datos . Un campo que es un número entre (x e y) debe validarse exactamente usando la regla exacta, y para un campo que es una cadena (texto): el caso es el patrón, por ejemplo, un nombre de usuario debe contener solo algunos caracteres, vamos a diga [a-zA-Z0-9_-.]. La longitud varía entre (x y n) donde x y n (enteros, x <= n). Regla: crear filtros exactos y reglas de validación son las mejores prácticas para mí.
Use otras herramientas: Aquí, también estaré de acuerdo con usted en que una declaración preparada (consulta parametrizada) y procedimientos almacenados. Las desventajas aquí son que estas formas requieren habilidades avanzadas que no existen para la mayoría de los usuarios. La idea básica aquí es distinguir entre la consulta SQL y los datos que se utilizan en su interior. Ambos enfoques se pueden usar incluso con datos inseguros, porque los datos ingresados por el usuario aquí no agregan nada a la consulta original, como (any o x = x).
Para obtener más información, lea la hoja de trucos de prevención de inyección SQL de OWASP .
Ahora, si es un usuario avanzado, comience a usar esta defensa como desee, pero, para los principiantes, si no pueden implementar rápidamente un procedimiento almacenado y prepararon la declaración, es mejor filtrar los datos de entrada tanto como puedan.
Finalmente, consideremos que un usuario envía este texto a continuación en lugar de ingresar su nombre de usuario:
Esta entrada puede verificarse temprano sin ninguna declaración preparada y procedimientos almacenados, pero para estar seguros, su uso comienza después del filtrado y validación de los datos del usuario.
El último punto es detectar comportamientos inesperados que requieren más esfuerzo y complejidad; No se recomienda para aplicaciones web normales.
El comportamiento inesperado en la entrada del usuario anterior es SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA y root. Una vez que se detectan estas palabras, puede evitar la entrada.
ACTUALIZACIÓN 1:
Un usuario comentó que esta publicación es inútil, ¡OK! Esto es lo que proporcionó OWASP.ORG :
Como ya sabrás, ¡reclamar un artículo debe estar respaldado por un argumento válido, al menos por una referencia! De lo contrario, se considera un ataque y un mal reclamo.
Actualización 2:
Del manual de PHP, PHP: Declaraciones preparadas - Manual :
Actualización 3:
Creé casos de prueba para saber cómo PDO y MySQLi envían la consulta al servidor MySQL cuando se usa una declaración preparada:
DOP:
Registro de consultas:
MySQLi:
Registro de consultas:
Está claro que una declaración preparada también está escapando de los datos, nada más.
Como también se menciona en la declaración anterior,
Por lo tanto, esto prueba que la validación de datos como
intval()
es una buena idea para valores enteros antes de enviar cualquier consulta. Además, prevenir los datos maliciosos del usuario antes de enviar la consulta es un enfoque correcto y válido .Consulte esta pregunta para obtener más detalles: PDO envía consultas sin formato a MySQL mientras Mysqli envía consultas preparadas, ambas producen el mismo resultado
Referencias
fuente
Utilizo tres formas diferentes para evitar que mi aplicación web sea vulnerable a la inyección de SQL.
mysql_real_escape_string()
, que es una función predefinida en PHP , y este código add barras invertidas a los siguientes caracteres:\x00
,\n
,\r
,\
,'
,"
y\x1a
. Pase los valores de entrada como parámetros para minimizar la posibilidad de inyección SQL.Espero que esto ayude.
Considere la siguiente consulta:
$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
mysql_real_escape_string () no protegerá aquí. Si usa comillas simples ('') alrededor de sus variables dentro de su consulta, eso es lo que lo protege contra esto. Aquí hay una solución a continuación para esto:
$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
Esta pregunta tiene algunas buenas respuestas sobre esto.
Sugiero, usar PDO es la mejor opción.
Editar:
mysql_real_escape_string()
está en desuso a partir de PHP 5.5.0. Use mysqli o PDO.Una alternativa a mysql_real_escape_string () es
Ejemplo:
fuente
Una manera simple sería utilizar un marco PHP como CodeIgniter o Laravel que tienen características incorporadas como filtrado y registro activo para que no tenga que preocuparse por estos matices.
fuente
Advertencia: el enfoque descrito en esta respuesta solo se aplica a escenarios muy específicos y no es seguro, ya que los ataques de inyección SQL no solo dependen de la capacidad de inyección
X=Y
.Si los atacantes intentan hackear el formulario a través de la
$_GET
variable de PHP o con la cadena de consulta de la URL, podrá atraparlos si no son seguros.Porque
1=1
,2=2
,1=2
,2=1
,1+1=2
, etc ... son las preguntas comunes a una base de datos SQL de un atacante. Quizás también lo usan muchas aplicaciones de hackeo.Pero debe tener cuidado, no debe reescribir una consulta segura desde su sitio. El código anterior le da una sugerencia, para reescribir o redirigir (depende de usted) esa cadena de consulta dinámica específica de piratería en una página que almacenará la dirección IP del atacante , o INCLUSO SUS COOKIES, historial, navegador o cualquier otro elemento sensible información, para que pueda tratar con ellos más tarde prohibiendo su cuenta o contactando a las autoridades.
fuente
1-1=0
? :)([0-9\-]+)=([0-9]+)
.Hay muchas respuestas para PHP y MySQL , pero aquí hay un código para PHP y Oracle para evitar la inyección de SQL, así como el uso regular de controladores oci8:
fuente
Una buena idea es usar un mapeador relacional de objetos como Idiorm :
¡No solo lo salva de las inyecciones de SQL, sino también de los errores de sintaxis! También admite colecciones de modelos con encadenamiento de métodos para filtrar o aplicar acciones a múltiples resultados a la vez y múltiples conexiones.
fuente
Usando PDO y MYSQLi es una buena práctica para evitar inyecciones de SQL, pero si realmente desea trabajar con las funciones y consultas de MySQL, sería mejor usar
mysql_real_escape_string
Hay más habilidades para evitar esto: como identificar: si la entrada es una cadena, un número, un carácter o una matriz, hay muchas funciones incorporadas para detectar esto. Además, sería mejor usar estas funciones para verificar los datos de entrada.
is_string
is_numeric
Y es mucho mejor usar esas funciones para verificar los datos de entrada
mysql_real_escape_string
.fuente
mysql_real_escape_string()
No es infalible .mysql_real_escape_string
ahora está en desuso, por lo que ya no es una opción viable. Será eliminado de PHP en el futuro. Es mejor pasar a lo que recomiendan las personas de PHP o MySQL.Escribí esta pequeña función hace varios años:
Esto permite ejecutar sentencias en una cadena de un solo trazo C # -ish. Formato como:
Se escapa teniendo en cuenta el tipo de variable. Si intenta parametrizar los nombres de tabla y columna, fallará ya que pone cada cadena entre comillas, lo que es una sintaxis no válida.
ACTUALIZACIÓN DE SEGURIDAD: la
str_replace
versión anterior permitía inyecciones agregando {#} tokens en los datos del usuario. Estapreg_replace_callback
versión no causa problemas si el reemplazo contiene estos tokens.fuente