sql >> Base de Datos >  >> RDS >> MariaDB

Cómo proteger su base de datos MySQL o MariaDB de la inyección SQL:segunda parte

En la primera parte de este blog, describimos cómo se puede usar ProxySQL para bloquear consultas entrantes que se consideraron peligrosas. Como viste en ese blog, lograr esto es muy fácil. Sin embargo, esta no es una solución completa. Es posible que deba diseñar una configuración aún más segura; es posible que desee bloquear todas las consultas y luego permitir que pasen solo algunas seleccionadas. Es posible usar ProxySQL para lograr eso. Echemos un vistazo a cómo se puede hacer.

Hay dos formas de implementar la lista blanca en ProxySQL. La primera, la histórica, sería crear una regla general que bloquee todas las consultas. Debería ser la última regla de consulta de la cadena. Un ejemplo a continuación:

Estamos haciendo coincidir cada cadena y generamos un mensaje de error. Esta es la única regla existente en este momento, impide que se ejecute cualquier consulta.

mysql> USE sbtest;

Database changed

mysql> SELECT * FROM sbtest1 LIMIT 10;

ERROR 1148 (42000): This query is not on the whitelist, you have to create a query rule before you'll be able to execute it.

mysql> SHOW TABLES FROM sbtest;

ERROR 1148 (42000): This query is not on the whitelist, you have to create a query rule before you'll be able to execute it.

mysql> SELECT 1;

ERROR 1148 (42000): This query is not on the whitelist, you have to create a query rule before you'll be able to execute it.

Como puede ver, no podemos ejecutar ninguna consulta. Para que nuestra aplicación funcione, tendríamos que crear reglas de consulta para todas las consultas que queremos permitir que se ejecuten. Se puede hacer por consulta, según el resumen o el patrón. También puede permitir el tráfico en función de otros factores:nombre de usuario, host del cliente, esquema. Permitamos SELECCIONES a una de las tablas:

Ahora podemos ejecutar consultas en esta tabla, pero no en ninguna otra:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

+------+------+

| id   | k |

+------+------+

| 7615 | 1942 |

| 3355 | 2310 |

+------+------+

2 rows in set (0.01 sec)

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

ERROR 1148 (42000): This query is not on the whitelist, you have to create a query rule before you'll be able to execute it.

El problema con este enfoque es que no se maneja de manera eficiente en ProxySQL, por lo tanto, en ProxySQL 2.0.9 viene con un nuevo mecanismo de firewall que incluye un nuevo algoritmo, enfocado en este caso de uso particular y, como tal, más eficiente. Veamos cómo podemos usarlo.

Primero, tenemos que instalar ProxySQL 2.0.9. Puede descargar paquetes manualmente desde https://github.com/sysown/proxysql/releases/tag/v2.0.9 o puede configurar el repositorio de ProxySQL.

Una vez hecho esto, podemos comenzar a investigarlo e intentar configurarlo para usar el firewall SQL.

El proceso en sí es bastante sencillo. En primer lugar, debe agregar un usuario a la tabla mysql_firewall_whitelist_users. Contiene todos los usuarios para los que se debe habilitar el firewall.

mysql> INSERT INTO mysql_firewall_whitelist_users (username, client_address, mode, comment) VALUES ('sbtest', '', 'DETECTING', '');

Query OK, 1 row affected (0.00 sec)

mysql> LOAD MYSQL FIREWALL TO RUNTIME;

Query OK, 0 rows affected (0.00 sec)

En la consulta anterior, agregamos el usuario 'sbtest' a la lista de usuarios que deberían tener habilitado el firewall. Es posible decir que solo las conexiones de un host dado se prueban contra las reglas del firewall. También puede tener tres modos:'APAGADO', cuando no se usa el firewall, 'DETECTANDO', donde las consultas incorrectas se registran pero no bloquean y 'PROTECCIÓN', donde las consultas no permitidas no se ejecutarán.

Habilitemos nuestro cortafuegos:

mysql> SET mysql-firewall_whitelist_enabled=1;

Query OK, 1 row affected (0.00 sec)

mysql> LOAD MYSQL VARIABLES TO RUNTIME;

Query OK, 0 rows affected (0.00 sec)

El cortafuegos ProxySQL se basa en el resumen de las consultas, no permite el uso de expresiones regulares. La mejor manera de recopilar datos sobre qué consultas deben permitirse es usar la tabla stats.stats_mysql_query_digest, donde puede recopilar consultas y sus resúmenes. Además de eso, ProxySQL 2.0.9 viene con una nueva tabla:history_mysql_query_digest, que es una extensión persistente de la tabla en memoria mencionada anteriormente. Puede configurar ProxySQL para almacenar datos en el disco de vez en cuando:

mysql> SET admin-stats_mysql_query_digest_to_disk=30;

Query OK, 1 row affected (0.00 sec)

Cada 30 segundos, los datos sobre las consultas se almacenarán en el disco. Veamos cómo va. Ejecutaremos un par de consultas y luego revisaremos sus resúmenes:

mysql> SELECT schemaname, username, digest, digest_text FROM history_mysql_query_digest;

+------------+----------+--------------------+-----------------------------------+

| schemaname | username | digest             | digest_text |

+------------+----------+--------------------+-----------------------------------+

| sbtest     | sbtest | 0x76B6029DCBA02DCA | SELECT id, k FROM sbtest1 LIMIT ? |

| sbtest     | sbtest | 0x1C46AE529DD5A40E | SELECT ?                          |

| sbtest     | sbtest | 0xB9697893C9DF0E42 | SELECT id, k FROM sbtest2 LIMIT ? |

+------------+----------+--------------------+-----------------------------------+

3 rows in set (0.00 sec)

Cuando configuramos el cortafuegos en modo 'DETECCIÓN', también veremos entradas en el registro:

2020-02-14 09:52:12 Query_Processor.cpp:2071:process_mysql_query(): [WARNING] Firewall detected unknown query with digest 0xB9697893C9DF0E42 from user [email protected]

2020-02-14 09:52:17 Query_Processor.cpp:2071:process_mysql_query(): [WARNING] Firewall detected unknown query with digest 0x76B6029DCBA02DCA from user [email protected]

2020-02-14 09:52:20 Query_Processor.cpp:2071:process_mysql_query(): [WARNING] Firewall detected unknown query with digest 0x1C46AE529DD5A40E from user [email protected]

Ahora, si queremos comenzar a bloquear consultas, debemos actualizar nuestro usuario y establecer el modo en 'PROTECCIÓN'. Esto bloqueará todo el tráfico, así que comencemos por incluir en la lista blanca las consultas anteriores. Luego habilitaremos el modo 'PROTECCIÓN':

mysql> INSERT INTO mysql_firewall_whitelist_rules (active, username, client_address, schemaname, digest, comment) VALUES (1, 'sbtest', '', 'sbtest', '0x76B6029DCBA02DCA', ''), (1, 'sbtest', '', 'sbtest', '0xB9697893C9DF0E42', ''), (1, 'sbtest', '', 'sbtest', '0x1C46AE529DD5A40E', '');

Query OK, 3 rows affected (0.00 sec)

mysql> UPDATE mysql_firewall_whitelist_users SET mode='PROTECTING' WHERE username='sbtest' AND client_address='';

Query OK, 1 row affected (0.00 sec)

mysql> LOAD MYSQL FIREWALL TO RUNTIME;

Query OK, 0 rows affected (0.00 sec)

mysql> SAVE MYSQL FIREWALL TO DISK;

Query OK, 0 rows affected (0.08 sec)

Eso es todo. Ahora podemos ejecutar consultas incluidas en la lista blanca:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

+------+------+

| id   | k |

+------+------+

| 7615 | 1942 |

| 3355 | 2310 |

+------+------+

2 rows in set (0.00 sec)

Pero no podemos ejecutar los que no están en la lista blanca:

mysql> SELECT id, k FROM sbtest3 LIMIT 2;

ERROR 1148 (42000): Firewall blocked this query

ProxySQL 2.0.9 viene con otra característica de seguridad interesante. Tiene libsqlinjection incorporado y puede habilitar la detección de posibles inyecciones de SQL. La detección se basa en los algoritmos de libsqlinjection. Esta función se puede habilitar ejecutando:

mysql> SET mysql-automatic_detect_sqli=1;

Query OK, 1 row affected (0.00 sec)

mysql> LOAD MYSQL VARIABLES TO RUNTIME;

Query OK, 0 rows affected (0.00 sec)

Funciona con el firewall de la siguiente manera:

  • Si el cortafuegos está habilitado y el usuario está en modo PROTECCIÓN, la detección de inyección SQL no se usa, ya que solo pueden pasar las consultas explícitamente incluidas en la lista blanca.
  • Si el firewall está habilitado y el usuario está en modo DETECCIÓN, las consultas incluidas en la lista blanca no se prueban para la inyección de SQL, todas las demás se probarán.
  • Si el cortafuegos está habilitado y el usuario está en modo 'APAGADO', se supone que todas las consultas están en la lista blanca y ninguna se probará para la inyección de SQL.
  • Si el cortafuegos está deshabilitado, todas las consultas se probarán para la detección de SQL.

Básicamente, se usa solo si el firewall está deshabilitado o para usuarios en modo 'DETECCIÓN'. Desafortunadamente, la detección de inyección SQL viene con una gran cantidad de falsos positivos. Puede usar la tabla mysql_firewall_whitelist_sqli_fingerprints para incluir en la lista blanca las huellas digitales para las consultas que se detectaron incorrectamente. Vamos a ver cómo funciona. Primero, deshabilitemos el firewall:

mysql> set mysql-firewall_whitelist_enabled=0;

Query OK, 1 row affected (0.00 sec)

mysql> LOAD MYSQL VARIABLES TO RUNTIME;

Query OK, 0 rows affected (0.00 sec)

Entonces, ejecutemos algunas consultas.

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

ERROR 2013 (HY000): Lost connection to MySQL server during query

De hecho, hay falsos positivos. En el registro pudimos encontrar:

2020-02-14 10:11:19 MySQL_Session.cpp:3393:handler(): [ERROR] SQLinjection detected with fingerprint of 'EnknB' from client [email protected] . Query listed below:

SELECT id, k FROM sbtest2 LIMIT 2

Ok, agreguemos esta huella digital a la tabla de la lista blanca:

mysql> INSERT INTO mysql_firewall_whitelist_sqli_fingerprints VALUES (1, 'EnknB');

Query OK, 1 row affected (0.00 sec)

mysql> LOAD MYSQL FIREWALL TO RUNTIME;

Query OK, 0 rows affected (0.00 sec)

Ahora finalmente podemos ejecutar esta consulta:

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

+------+------+

| id   | k |

+------+------+

|   84 | 2456 |

| 6006 | 2588 |

+------+------+

2 rows in set (0.01 sec)

Intentamos ejecutar la carga de trabajo de sysbench, esto dio como resultado que se agregaran dos huellas digitales más a la tabla de la lista blanca:

2020-02-14 10:15:55 MySQL_Session.cpp:3393:handler(): [ERROR] SQLinjection detected with fingerprint of 'Enknk' from client [email protected] . Query listed below:

SELECT c FROM sbtest21 WHERE id=49474

2020-02-14 10:16:02 MySQL_Session.cpp:3393:handler(): [ERROR] SQLinjection detected with fingerprint of 'Ef(n)' from client [email protected] . Query listed below:

SELECT SUM(k) FROM sbtest32 WHERE id BETWEEN 50053 AND 50152

Queríamos ver si esta inyección automática de SQL puede protegernos contra nuestro buen amigo, Booby Tables.

mysql> CREATE TABLE school.students (id INT, name VARCHAR(40));

Query OK, 0 rows affected (0.07 sec)

mysql> INSERT INTO school.students VALUES (1, 'Robert');DROP TABLE students;--

Query OK, 1 row affected (0.01 sec)

Query OK, 0 rows affected (0.04 sec)

mysql> SHOW TABLES FROM school;

Empty set (0.01 sec)

Desafortunadamente, no realmente. Tenga en cuenta que esta característica se basa en algoritmos forenses automatizados, está lejos de ser perfecta. Puede venir como una capa adicional de defensa, pero nunca podrá reemplazar el firewall correctamente mantenido creado por alguien que conoce la aplicación y sus consultas.

Esperamos que después de leer esta breve serie de dos partes, tenga una mejor comprensión de cómo puede proteger su base de datos contra la inyección de SQL y los intentos maliciosos (o simplemente errores del usuario) usando ProxySQL. Si tiene más ideas, nos encantaría saber de usted en los comentarios.