MariaDB
 sql >> Datenbank >  >> RDS >> MariaDB

So schützen Sie Ihre MySQL- oder MariaDB-Datenbank vor SQL-Injection:Teil Zwei

Im ersten Teil dieses Blogs haben wir beschrieben, wie ProxySQL verwendet werden kann, um eingehende Abfragen zu blockieren, die als gefährlich eingestuft wurden. Wie Sie in diesem Blog gesehen haben, ist es sehr einfach, dies zu erreichen. Dies ist jedoch keine vollständige Lösung. Möglicherweise müssen Sie ein noch strenger gesichertes Setup entwerfen - Sie möchten möglicherweise alle Abfragen blockieren und dann nur einige ausgewählte passieren lassen. Es ist möglich, ProxySQL zu verwenden, um dies zu erreichen. Schauen wir uns an, wie es gemacht werden kann.

Es gibt zwei Möglichkeiten, Whitelists in ProxySQL zu implementieren. Erstens, die historische, wäre die Erstellung einer Auffangregel, die alle Abfragen blockiert. Es sollte die letzte Abfrageregel in der Kette sein. Ein Beispiel unten:

Wir gleichen jede Zeichenfolge ab und generieren eine Fehlermeldung. Dies ist die einzige derzeit existierende Regel, sie verhindert, dass eine Abfrage ausgeführt wird.

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.

Wie Sie sehen, können wir keine Abfragen ausführen. Damit unsere Anwendung funktioniert, müssten wir Abfrageregeln für alle Abfragen erstellen, deren Ausführung wir zulassen möchten. Dies kann pro Abfrage erfolgen, basierend auf dem Digest oder Muster. Sie können Datenverkehr auch basierend auf anderen Faktoren zulassen:Benutzername, Client-Host, Schema. Lassen Sie uns SELECTs für eine der Tabellen zulassen:

Jetzt können wir Abfragen für diese Tabelle ausführen, aber für keine andere:

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.

Das Problem bei diesem Ansatz ist, dass er in ProxySQL nicht effizient gehandhabt wird, daher kommt ProxySQL 2.0.9 mit einem neuen Firewall-Mechanismus, der einen neuen Algorithmus enthält, der sich auf diesen speziellen Anwendungsfall und damit auf mehr konzentriert effizient. Mal sehen, wie wir es verwenden können.

Zuerst müssen wir ProxySQL 2.0.9 installieren. Sie können Pakete manuell von https://github.com/sysown/proxysql/releases/tag/v2.0.9 herunterladen oder das ProxySQL-Repository einrichten.

Sobald dies erledigt ist, können wir uns damit befassen und versuchen, es für die Verwendung der SQL-Firewall zu konfigurieren.

Der Prozess selbst ist ganz einfach. Zunächst müssen Sie der Tabelle mysql_firewall_whitelist_users einen Benutzer hinzufügen. Es enthält alle Benutzer, für die die Firewall aktiviert werden soll.

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)

In der obigen Abfrage haben wir den Benutzer „sbtest“ zur Liste der Benutzer hinzugefügt, für die die Firewall aktiviert sein sollte. Es ist möglich festzustellen, dass nur Verbindungen von einem bestimmten Host anhand der Firewall-Regeln getestet werden. Sie können auch drei Modi haben:„AUS“, wenn die Firewall nicht verwendet wird, „ERKENNEN“, wenn falsche Abfragen protokolliert, aber nicht blockiert werden, und „SCHÜTZEN“, wenn unzulässige Abfragen nicht ausgeführt werden.

Aktivieren wir unsere 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)

Die ProxySQL-Firewall basiert auf dem Digest der Abfragen, sie erlaubt keine Verwendung regulärer Ausdrücke. Der beste Weg, um Daten darüber zu sammeln, welche Abfragen zulässig sein sollten, ist die Verwendung der Tabelle stats.stats_mysql_query_digest, in der Sie Abfragen und ihre Digests sammeln können. Darüber hinaus enthält ProxySQL 2.0.9 eine neue Tabelle:history_mysql_query_digest, die eine dauerhafte Erweiterung der zuvor erwähnten In-Memory-Tabelle ist. Sie können ProxySQL so konfigurieren, dass Daten von Zeit zu Zeit auf der Festplatte gespeichert werden:

mysql> SET admin-stats_mysql_query_digest_to_disk=30;

Query OK, 1 row affected (0.00 sec)

Alle 30 Sekunden werden Daten über Abfragen auf der Festplatte gespeichert. Mal sehen, wie es geht. Wir führen einige Abfragen aus und prüfen dann ihre Digests:

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)

Wenn wir die Firewall in den Modus „ERKENNEN“ versetzen, sehen wir auch Einträge im Protokoll:

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]

Wenn wir nun damit beginnen möchten, Abfragen zu blockieren, sollten wir unseren Benutzer aktualisieren und den Modus auf „SCHÜTZEN“ setzen. Dadurch wird der gesamte Datenverkehr blockiert. Beginnen wir also mit dem Whitelisting von Abfragen oben. Dann aktivieren wir den „SCHUTZ“-Modus:

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)

Das ist es. Jetzt können wir Whitelist-Abfragen ausführen:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

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

| id   | k |

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

| 7615 | 1942 |

| 3355 | 2310 |

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

2 rows in set (0.00 sec)

Aber wir können nicht diejenigen ausführen, die nicht auf der weißen Liste stehen:

mysql> SELECT id, k FROM sbtest3 LIMIT 2;

ERROR 1148 (42000): Firewall blocked this query

ProxySQL 2.0.9 enthält noch ein weiteres interessantes Sicherheitsfeature. Es hat libsqlinjection eingebettet und Sie können die Erkennung möglicher SQL-Injektionen aktivieren. Die Erkennung basiert auf den Algorithmen der libsqlinjection. Diese Funktion kann durch Ausführen von:

aktiviert werden
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)

Es funktioniert folgendermaßen mit der Firewall:

  • Wenn die Firewall aktiviert ist und sich der Benutzer im PROTECTING-Modus befindet, wird die SQL-Injection-Erkennung nicht verwendet, da nur ausdrücklich auf die Whitelist gesetzte Abfragen passieren können.
  • Wenn die Firewall aktiviert ist und sich der Benutzer im ERKENNUNGS-Modus befindet, werden Whitelist-Abfragen nicht auf SQL-Injection getestet, alle anderen werden getestet.
  • Wenn die Firewall aktiviert ist und sich der Benutzer im Modus „AUS“ befindet, wird davon ausgegangen, dass alle Abfragen auf der weißen Liste stehen und keine auf SQL-Injection getestet wird.
  • Wenn die Firewall deaktiviert ist, werden alle Abfragen auf SQL-Intection getestet.

Grundsätzlich wird es nur verwendet, wenn die Firewall deaktiviert ist oder für Benutzer im ‘DETECTING’-Modus. Die SQL-Injection-Erkennung ist leider mit ziemlich vielen Fehlalarmen verbunden. Sie können die Tabelle mysql_firewall_whitelist_sqli_fingerprints verwenden, um Fingerabdrücke für Abfragen, die falsch erkannt wurden, auf die Whitelist zu setzen. Mal sehen, wie es funktioniert. Lassen Sie uns zuerst die Firewall deaktivieren:

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)

Dann lassen Sie uns einige Abfragen ausführen.

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

Tatsächlich gibt es Fehlalarme. Im Protokoll konnten wir Folgendes finden:

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, fügen wir diesen Fingerabdruck der Whitelist-Tabelle hinzu:

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)

Jetzt können wir endlich diese Abfrage ausführen:

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

| id   | k |

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

|   84 | 2456 |

| 6006 | 2588 |

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

2 rows in set (0.01 sec)

Wir haben versucht, die Sysbench-Arbeitslast auszuführen, was dazu führte, dass zwei weitere Fingerabdrücke zur Whitelist-Tabelle hinzugefügt wurden:

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

Wir wollten sehen, ob uns diese automatisierte SQL-Einschleusung vor unserem guten Freund Booby Tables schützen kann.

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)

Leider nicht wirklich. Bitte denken Sie daran, dass diese Funktion auf automatisierten forensischen Algorithmen basiert und bei weitem nicht perfekt ist. Es kann als zusätzliche Verteidigungsebene dienen, aber es wird niemals eine ordnungsgemäß gewartete Firewall ersetzen können, die von jemandem erstellt wurde, der die Anwendung und ihre Abfragen kennt.

Wir hoffen, dass Sie nach dem Lesen dieser kurzen, zweiteiligen Serie besser verstehen, wie Sie Ihre Datenbank mit ProxySQL vor SQL-Injection und böswilligen Versuchen (oder einfach nur Benutzerfehlern) schützen können. Wenn Sie weitere Ideen haben, würden wir uns freuen, von Ihnen in den Kommentaren zu hören.