MariaDB
 sql >> Baza danych >  >> RDS >> MariaDB

Jak chronić bazę danych MySQL lub MariaDB przed wstrzyknięciem SQL:część druga

W pierwszej części tego bloga opisaliśmy, jak ProxySQL może być używany do blokowania przychodzących zapytań, które zostały uznane za niebezpieczne. Jak widzieliście na tym blogu, osiągnięcie tego jest bardzo łatwe. Nie jest to jednak pełne rozwiązanie. Być może będziesz musiał zaprojektować jeszcze lepiej zabezpieczoną konfigurację — możesz chcieć zablokować wszystkie zapytania, a następnie zezwolić na przepuszczanie tylko wybranych. W tym celu można użyć ProxySQL. Zobaczmy, jak można to zrobić.

Istnieją dwa sposoby implementacji białej listy w ProxySQL. Pierwsza, historyczna, polegałaby na utworzeniu reguły typu catch-all, która zablokuje wszystkie zapytania. Powinna to być ostatnia reguła zapytania w łańcuchu. Przykład poniżej:

Dopasowujemy każdy ciąg i generujemy komunikat o błędzie. Jest to jedyna reguła istniejąca w tej chwili, która uniemożliwia wykonanie jakiegokolwiek zapytania.

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 widać, nie możemy uruchamiać żadnych zapytań. Aby nasza aplikacja działała musielibyśmy stworzyć reguły zapytań dla wszystkich zapytań, które chcemy zezwolić na wykonanie. Można to zrobić na zapytanie, na podstawie skrótu lub wzorca. Możesz także zezwolić na ruch na podstawie innych czynników:nazwy użytkownika, hosta klienta, schematu. Zezwólmy na SELECT do jednej z tabel:

Teraz możemy wykonywać zapytania na tej tabeli, ale nie na żadnej innej:

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.

Problem z tym podejściem polega na tym, że nie jest ono efektywnie obsługiwane w ProxySQL, dlatego w ProxySQL 2.0.9 dostępny jest nowy mechanizm zapory ogniowej, który zawiera nowy algorytm, skoncentrowany na tym konkretnym przypadku użycia i jako taki więcej wydajny. Zobaczmy, jak możemy to wykorzystać.

Najpierw musimy zainstalować ProxySQL 2.0.9. Możesz pobrać pakiety ręcznie z https://github.com/sysown/proxysql/releases/tag/v2.0.9 lub możesz skonfigurować repozytorium ProxySQL.

Gdy to zrobimy, możemy zacząć się temu przyglądać i spróbować skonfigurować go do korzystania z zapory SQL.

Sam proces jest dość prosty. Przede wszystkim musisz dodać użytkownika do tabeli mysql_firewall_whitelist_users. Zawiera wszystkich użytkowników, dla których powinna być włączona zapora.

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)

W powyższym zapytaniu dodaliśmy użytkownika „sbtest” do listy użytkowników, którzy powinni mieć włączoną zaporę sieciową. Można stwierdzić, że tylko połączenia z danego hosta są testowane pod kątem zgodności z regułami zapory. Możesz także mieć trzy tryby:„WYŁĄCZONY”, gdy zapora nie jest używana, „WYKRYWANIE”, w którym nieprawidłowe zapytania są rejestrowane, ale nie są blokowane, oraz „OCHRONA”, w którym niedozwolone zapytania nie będą wykonywane.

Włączmy naszą zaporę sieciową:

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)

Zapora ProxySQL bazuje na streszczeniu zapytań, nie pozwala na użycie wyrażeń regularnych. Najlepszym sposobem zbierania danych o tym, które zapytania powinny być dozwolone, jest użycie tabeli stats.stats_mysql_query_digest, w której możesz zbierać zapytania i ich skróty. Ponadto ProxySQL 2.0.9 zawiera nową tabelę:history_mysql_query_digest, która jest trwałym rozszerzeniem wspomnianej wcześniej tabeli w pamięci. Możesz skonfigurować ProxySQL do przechowywania danych na dysku od czasu do czasu:

mysql> SET admin-stats_mysql_query_digest_to_disk=30;

Query OK, 1 row affected (0.00 sec)

Co 30 sekund dane o zapytaniach będą przechowywane na dysku. Zobaczmy jak idzie. Wykonamy kilka zapytań, a następnie sprawdzimy ich skróty:

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)

Gdy ustawimy zaporę w trybie „WYKRYWANIE”, zobaczymy również wpisy w dzienniku:

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]

Teraz, jeśli chcemy rozpocząć blokowanie zapytań, powinniśmy zaktualizować naszego użytkownika i ustawić tryb na „OCHRONA”. Spowoduje to zablokowanie całego ruchu, więc zacznijmy od dodania zapytań powyżej. Następnie włączymy tryb „OCHRONA”:

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 wszystko. Teraz możemy wykonywać zapytania z białej listy:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

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

| id   | k |

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

| 7615 | 1942 |

| 3355 | 2310 |

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

2 rows in set (0.00 sec)

Ale nie możemy wykonać tych, które nie znajdują się na białej liście:

mysql> SELECT id, k FROM sbtest3 LIMIT 2;

ERROR 1148 (42000): Firewall blocked this query

ProxySQL 2.0.9 zawiera jeszcze jedną interesującą funkcję bezpieczeństwa. Ma wbudowaną bibliotekę libsqlinjection i można włączyć wykrywanie możliwych wstrzyknięć SQL. Wykrywanie opiera się na algorytmach z libsqlinjection. Tę funkcję można włączyć, uruchamiając:

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)

Działa z zaporą sieciową w następujący sposób:

  • Jeśli zapora jest włączona, a użytkownik jest w trybie OCHRONY, wykrywanie iniekcji SQL nie jest używane, ponieważ mogą przechodzić tylko zapytania z jawnej białej listy.
  • Jeśli zapora jest włączona, a użytkownik jest w trybie WYKRYWANIA, zapytania z białej listy nie są testowane pod kątem wstrzyknięcia SQL, wszystkie inne zostaną przetestowane.
  • Jeśli zapora jest włączona, a użytkownik jest w trybie „WYŁĄCZONY”, zakłada się, że wszystkie zapytania znajdują się na białej liście i żadne nie zostanie przetestowane pod kątem wstrzyknięcia SQL.
  • Jeśli zapora jest wyłączona, wszystkie zapytania zostaną przetestowane pod kątem wykrycia SQL.

Zasadniczo jest używany tylko wtedy, gdy zapora jest wyłączona lub dla użytkowników w trybie WYKRYWANIA. Niestety, wykrywanie iniekcji SQL wiąże się z dużą liczbą fałszywych alarmów. Możesz użyć tabeli mysql_firewall_whitelist_sqli_fingerprints do białej listy odcisków palców dla zapytań, które zostały wykryte nieprawidłowo. Zobaczmy, jak to działa. Najpierw wyłączmy zaporę sieciową:

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)

W takim razie uruchommy kilka zapytań.

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

Rzeczywiście, istnieją fałszywe alarmy. W dzienniku mogliśmy znaleźć:

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, dodajmy ten odcisk palca do tabeli białej listy:

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)

Teraz możemy wreszcie wykonać to zapytanie:

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

| id   | k |

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

|   84 | 2456 |

| 6006 | 2588 |

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

2 rows in set (0.01 sec)

Próbowaliśmy uruchomić obciążenie sysbench, co spowodowało dodanie dwóch dodatkowych odcisków palców do tabeli białej listy:

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

Chcieliśmy sprawdzić, czy to automatyczne wstrzyknięcie SQL może ochronić nas przed naszym dobrym przyjacielem, 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)

Niestety niezupełnie. Należy pamiętać, że ta funkcja opiera się na automatycznych algorytmach kryminalistycznych i jest daleka od doskonałości. Może stanowić dodatkową warstwę ochrony, ale nigdy nie będzie w stanie zastąpić prawidłowo obsługiwanej zapory ogniowej stworzonej przez kogoś, kto zna aplikację i jej zapytania.

Mamy nadzieję, że po przeczytaniu tej krótkiej, dwuczęściowej serii lepiej zrozumiesz, w jaki sposób możesz chronić swoją bazę danych przed wstrzyknięciem SQL i złośliwymi próbami (lub po prostu zwykłymi błędami użytkownika) przy użyciu ProxySQL. Jeśli masz więcej pomysłów, chcielibyśmy usłyszeć od Ciebie w komentarzach.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MariaDB JSON_SEARCH() Objaśnienie

  2. Co to jest MariaDB Enterprise i jak nim zarządzać za pomocą ClusterControl?

  3. MariaDB ROUND() kontra PODŁOGA()

  4. Przedstawiamy ClusterControl 1.5 — wyposażony w automatyczną weryfikację kopii zapasowych i przesyłanie do chmury

  5. Jak dodać AM/PM do wartości czasu lub daty i godziny w MariaDB?