In het eerste deel van deze blog hebben we beschreven hoe ProxySQL kan worden gebruikt om inkomende zoekopdrachten te blokkeren die als gevaarlijk werden beschouwd. Zoals je in die blog hebt gezien, is dit heel eenvoudig. Dit is echter geen volledige oplossing. Mogelijk moet u een nog strenger beveiligde installatie ontwerpen - u wilt misschien alle query's blokkeren en vervolgens slechts enkele geselecteerde doorlaten. Het is mogelijk om ProxySQL te gebruiken om dat te bereiken. Laten we eens kijken hoe het kan.
Er zijn twee manieren om de witte lijst in ProxySQL te implementeren. Ten eerste, de historische, zou zijn om een allesomvattende regel te maken die alle zoekopdrachten blokkeert. Het zou de laatste queryregel in de keten moeten zijn. Een voorbeeld hieronder:
We matchen elke string en genereren een foutmelding. Dit is de enige regel die op dit moment bestaat, het voorkomt dat een zoekopdracht wordt uitgevoerd.
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.
Zoals je kunt zien, kunnen we geen zoekopdrachten uitvoeren. Om onze toepassing te laten werken, zouden we queryregels moeten maken voor alle query's die we willen laten uitvoeren. Het kan per query, op basis van de samenvatting of het patroon. U kunt ook verkeer toestaan op basis van de andere factoren:gebruikersnaam, clienthost, schema. Laten we SELECT's toestaan aan een van de tabellen:
Nu kunnen we query's uitvoeren op deze tabel, maar niet op een 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.
Het probleem met deze aanpak is dat het niet efficiënt wordt afgehandeld in ProxySQL, daarom wordt ProxySQL 2.0.9 geleverd met een nieuw mechanisme van firewalling met een nieuw algoritme, gericht op dit specifieke gebruik en als zodanig meer efficiënt. Laten we eens kijken hoe we het kunnen gebruiken.
Eerst moeten we ProxySQL 2.0.9 installeren. U kunt pakketten handmatig downloaden van https://github.com/sysown/proxysql/releases/tag/v2.0.9 of u kunt de ProxySQL-repository instellen.
Zodra dit is gebeurd, kunnen we het gaan onderzoeken en proberen het te configureren om SQL-firewall te gebruiken.
Het proces zelf is vrij eenvoudig. Allereerst moet u een gebruiker toevoegen aan de tabel mysql_firewall_whitelist_users. Het bevat alle gebruikers waarvoor de firewall moet worden ingeschakeld.
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 de bovenstaande zoekopdracht hebben we 'sbtest'-gebruiker toegevoegd aan de lijst met gebruikers die de firewall moeten hebben ingeschakeld. Het is mogelijk om te zien dat alleen verbindingen van een bepaalde host worden getoetst aan de firewallregels. U kunt ook drie modi hebben:'UIT', wanneer de firewall niet wordt gebruikt, 'DETECTING', waarbij onjuiste zoekopdrachten worden geregistreerd maar niet worden geblokkeerd en 'BESCHERMING', waarbij niet-toegestane zoekopdrachten niet worden uitgevoerd.
Laten we onze firewall inschakelen:
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 is gebaseerd op de samenvatting van de query's, het staat het gebruik van reguliere expressies niet toe. De beste manier om gegevens te verzamelen over welke query's moeten worden toegestaan, is door de tabel stats.stats_mysql_query_digest te gebruiken, waar u query's en hun samenvattingen kunt verzamelen. Bovendien komt ProxySQL 2.0.9 met een nieuwe tabel:history_mysql_query_digest, wat een permanente uitbreiding is op de eerder genoemde in-memory tabel. U kunt ProxySQL configureren om van tijd tot tijd gegevens op schijf op te slaan:
mysql> SET admin-stats_mysql_query_digest_to_disk=30;
Query OK, 1 row affected (0.00 sec)
Elke 30 seconden worden gegevens over zoekopdrachten op schijf opgeslagen. Laten we zien hoe het gaat. We zullen een aantal zoekopdrachten uitvoeren en vervolgens hun samenvattingen controleren:
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)
Als we de firewall in de modus 'DETECTING' zetten, zien we ook vermeldingen in het logboek:
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]
Als we nu query's willen blokkeren, moeten we onze gebruiker updaten en de modus instellen op 'BESCHERMEN'. Dit blokkeert al het verkeer, dus laten we beginnen met het op de witte lijst zetten van bovenstaande zoekopdrachten. Dan schakelen we de 'BESCHERMENDE' modus in:
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)
Dat is het. Nu kunnen we zoekopdrachten op de witte lijst uitvoeren:
mysql> SELECT id, k FROM sbtest1 LIMIT 2;
+------+------+
| id | k |
+------+------+
| 7615 | 1942 |
| 3355 | 2310 |
+------+------+
2 rows in set (0.00 sec)
Maar we kunnen niet-op de witte lijst geplaatste programma's niet uitvoeren:
mysql> SELECT id, k FROM sbtest3 LIMIT 2;
ERROR 1148 (42000): Firewall blocked this query
ProxySQL 2.0.9 wordt geleverd met nog een andere interessante beveiligingsfunctie. Het heeft ingebouwde libsqlinjection en u kunt de detectie van mogelijke SQL-injecties inschakelen. Detectie is gebaseerd op de algoritmen uit de libsqlinjection. Deze functie kan worden ingeschakeld door het volgende uit te voeren:
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)
Het werkt op de volgende manier met de firewall:
- Als de firewall is ingeschakeld en de gebruiker zich in de BESCHERMENDE modus bevindt, wordt detectie van SQL-injectie niet gebruikt, omdat alleen expliciet op de witte lijst geplaatste zoekopdrachten kunnen passeren.
- Als de firewall is ingeschakeld en de gebruiker zich in de DETECTIE-modus bevindt, worden zoekopdrachten op de witte lijst niet getest voor SQL-injectie, alle andere worden wel getest.
- Als de firewall is ingeschakeld en de gebruiker in de 'UIT'-modus staat, wordt aangenomen dat alle zoekopdrachten op de witte lijst staan en dat er geen enkele wordt getest op SQL-injectie.
- Als de firewall is uitgeschakeld, worden alle query's getest op SQL-detectie.
In principe wordt het alleen gebruikt als de firewall is uitgeschakeld of voor gebruikers in de modus 'DETECTIE'. Detectie van SQL-injectie gaat helaas gepaard met nogal wat valse positieven. U kunt de tabel mysql_firewall_whitelist_sqli_fingerprints gebruiken om vingerafdrukken op de witte lijst te zetten voor zoekopdrachten die onjuist zijn gedetecteerd. Laten we eens kijken hoe het werkt. Laten we eerst de firewall uitschakelen:
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)
Laten we dan wat zoekopdrachten uitvoeren.
mysql> SELECT id, k FROM sbtest2 LIMIT 2;
ERROR 2013 (HY000): Lost connection to MySQL server during query
Er zijn inderdaad valse positieven. In het logboek konden we vinden:
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, laten we deze vingerafdruk aan de witte lijsttabel toevoegen:
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)
Nu kunnen we eindelijk deze query uitvoeren:
mysql> SELECT id, k FROM sbtest2 LIMIT 2;
+------+------+
| id | k |
+------+------+
| 84 | 2456 |
| 6006 | 2588 |
+------+------+
2 rows in set (0.01 sec)
We hebben geprobeerd de sysbench-workload uit te voeren, dit resulteerde in nog twee vingerafdrukken die aan de witte lijsttabel werden toegevoegd:
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
We wilden zien of deze geautomatiseerde SQL-injectie ons kan beschermen tegen onze goede vriend, 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)
Helaas niet echt. Houd er rekening mee dat deze functie is gebaseerd op geautomatiseerde forensische algoritmen en verre van perfect is. Het kan als een extra verdedigingslaag komen, maar het zal nooit een goed onderhouden firewall kunnen vervangen die is gemaakt door iemand die de applicatie en zijn vragen kent.
We hopen dat u na het lezen van deze korte, tweedelige serie een beter begrip heeft van hoe u uw database kunt beschermen tegen SQL-injectie en kwaadwillende pogingen (of gewoon gebruikersfouten) met behulp van ProxySQL. Als je meer ideeën hebt, horen we graag van je in de reacties.