sql >> Databáze >  >> RDS >> MariaDB

Jak chránit databázi MySQL nebo MariaDB před SQL Injection:Část druhá

V první části tohoto blogu jsme popsali, jak lze použít ProxySQL k blokování příchozích dotazů, které byly považovány za nebezpečné. Jak jste viděli na tomto blogu, dosáhnout toho je velmi snadné. Toto však není úplné řešení. Možná budete muset navrhnout ještě přísněji zabezpečené nastavení – možná budete chtít zablokovat všechny dotazy a poté umožnit průchod jen některým vybraným. K tomu je možné použít ProxySQL. Pojďme se podívat, jak to lze provést.

Existují dva způsoby, jak implementovat whitelist v ProxySQL. Za prvé, historické, by bylo vytvořit univerzální pravidlo, které bude blokovat všechny dotazy. Mělo by to být poslední pravidlo dotazu v řetězci. Příklad níže:

Porovnáváme každý řetězec a vygenerujeme chybovou zprávu. Toto je jediné pravidlo, které v současné době existuje, brání provedení jakéhokoli dotazu.

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.

Jak vidíte, nemůžeme spouštět žádné dotazy. Aby naše aplikace fungovala, museli bychom vytvořit pravidla dotazů pro všechny dotazy, které chceme povolit provádět. Lze to provést na dotaz na základě výtahu nebo vzoru. Můžete také povolit provoz na základě dalších faktorů:uživatelské jméno, hostitel klienta, schéma. Povolme SELECTy do jedné z tabulek:

Nyní můžeme provádět dotazy na tuto tabulku, ale ne na žádnou jinou:

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.

Problém s tímto přístupem je, že v ProxySQL není efektivně zpracován, proto v ProxySQL 2.0.9 přichází s novým mechanismem firewallingu, který obsahuje nový algoritmus, zaměřený na tento konkrétní případ použití a jako takový více účinný. Podívejme se, jak to můžeme použít.

Nejprve musíme nainstalovat ProxySQL 2.0.9. Balíčky si můžete stáhnout ručně z https://github.com/sysown/proxysql/releases/tag/v2.0.9 nebo můžete nastavit úložiště ProxySQL.

Jakmile to bude hotové, můžeme se tím začít zabývat a pokusit se jej nakonfigurovat tak, aby používal SQL firewall.

Samotný proces je docela snadný. Nejprve musíte přidat uživatele do tabulky mysql_firewall_whitelist_users. Obsahuje všechny uživatele, pro které by měl být firewall povolen.

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)

V dotazu výše jsme přidali uživatele „sbtest“ do seznamu uživatelů, kteří by měli mít povolenou bránu firewall. Je možné říci, že pouze spojení z daného počítače jsou testována podle pravidel firewallu. Můžete mít také tři režimy:‚OFF‘, když se nepoužívá firewall, ‚DETECTING‘, kde jsou protokolovány nesprávné dotazy, ale nejsou blokovány, a ‚PROTECTING‘, kdy se nepovolené dotazy nebudou provádět.

Pojďme povolit náš firewall:

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)

ProxySQL firewall je založen na přehledu dotazů, neumožňuje použití regulárních výrazů. Nejlepší způsob, jak shromáždit data o tom, které dotazy by měly být povoleny, je použít tabulku stats.stats_mysql_query_digest, kde můžete shromažďovat dotazy a jejich výtahy. Kromě toho přichází ProxySQL 2.0.9 s novou tabulkou:history_mysql_query_digest, což je trvalé rozšíření výše uvedené tabulky v paměti. ProxySQL můžete nakonfigurovat tak, aby čas od času ukládala data na disk:

mysql> SET admin-stats_mysql_query_digest_to_disk=30;

Query OK, 1 row affected (0.00 sec)

Každých 30 sekund budou data o dotazech uložena na disk. Podívejme se, jak to jde. Provedeme několik dotazů a poté zkontrolujeme jejich výtahy:

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)

Když bránu firewall nastavíme do režimu „DETEKCE“, uvidíme také záznamy v protokolu:

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]

Nyní, pokud chceme začít blokovat dotazy, měli bychom aktualizovat našeho uživatele a nastavit režim na „OCHRANA“. To zablokuje veškerý provoz, takže začněme přidáním dotazů na seznam povolených výše. Poté povolíme režim „OCHRANA“:

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)

To je ono. Nyní můžeme provádět dotazy ze seznamu povolených:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

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

| id   | k |

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

| 7615 | 1942 |

| 3355 | 2310 |

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

2 rows in set (0.00 sec)

Nemůžeme však spustit ty, které nejsou na seznamu povolených:

mysql> SELECT id, k FROM sbtest3 LIMIT 2;

ERROR 1148 (42000): Firewall blocked this query

ProxySQL 2.0.9 přichází s další zajímavou bezpečnostní funkcí. Má vestavěnou knihovnu libsqlinjection a můžete povolit detekci možných injekcí SQL. Detekce je založena na algoritmech z libsqlinjection. Tuto funkci lze aktivovat spuštěním:

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)

Funguje s firewallem následujícím způsobem:

  • Pokud je povolena brána firewall a uživatel je v režimu OCHRANY, detekce vkládání SQL se nepoužije, protože jím mohou projít pouze dotazy, které jsou výslovně uvedeny na seznamu povolených.
  • Pokud je povolena brána firewall a uživatel je v režimu DETEKCE, dotazy na seznamu povolených se netestují na vkládání SQL, budou testovány všechny ostatní.
  • Pokud je povolena brána firewall a uživatel je v režimu „VYPNUTO“, předpokládá se, že všechny dotazy jsou na seznamu povolených a žádný nebude testován na vložení SQL.
  • Pokud je brána firewall deaktivována, všechny dotazy budou testovány na přítomnost SQL.

V zásadě se používá pouze v případě, že je vypnutá brána firewall nebo pro uživatele v režimu „DETEKCE“. Detekce injekce SQL bohužel přichází s poměrně velkým množstvím falešných poplachů. Pomocí tabulky mysql_firewall_whitelist_sqli_fingerprints můžete přidat otisky prstů na seznam povolených pro dotazy, které byly detekovány nesprávně. Podívejme se, jak to funguje. Nejprve deaktivujeme 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)

Pak spusťte nějaké dotazy.

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

Skutečně existují falešná pozitiva. V logu jsme našli:

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

Dobře, pojďme přidat tento otisk do tabulky povolených:

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)

Nyní můžeme konečně provést tento dotaz:

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

| id   | k |

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

|   84 | 2456 |

| 6006 | 2588 |

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

2 rows in set (0.01 sec)

Pokusili jsme se spustit zátěž sysbench, což vedlo k přidání dalších dvou otisků prstů do tabulky whitelist:

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

Chtěli jsme zjistit, zda nás tato automatizovaná injekce SQL může ochránit před naším dobrým přítelem, 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)

Bohužel ve skutečnosti ne. Mějte prosím na paměti, že tato funkce je založena na automatizovaných forenzních algoritmech a není zdaleka dokonalá. Může přijít jako další vrstva obrany, ale nikdy nebude schopen nahradit správně udržovaný firewall vytvořený někým, kdo zná aplikaci a její dotazy.

Doufáme, že po přečtení této krátké dvoudílné série lépe porozumíte tomu, jak můžete pomocí ProxySQL chránit svou databázi proti SQL injection a škodlivým pokusům (nebo prostě uživatelským chybám). Pokud máte další nápady, budeme rádi, když se ozvete v komentářích.


  1. Najděte všechny nečíselné hodnoty ve sloupci v MariaDB

  2. Správa vysoké dostupnosti v PostgreSQL – Část III:Patroni

  3. Jak mohu pracovat s vysoce přesnými desetinnými místy v PHP

  4. Jak nastavit databázi WordPress MySQL v cloudu