sql >> Databasteknik >  >> RDS >> MariaDB

Hur du skyddar din MySQL- eller MariaDB-databas från SQL-injektion:Del två

I den första delen av den här bloggen beskrev vi hur ProxySQL kan användas för att blockera inkommande frågor som ansågs farliga. Som du såg i den bloggen är det väldigt enkelt att uppnå detta. Detta är dock inte en fullständig lösning. Du kan behöva designa en ännu bättre säkrad installation - du kanske vill blockera alla frågor och sedan låta bara några utvalda passera. Det är möjligt att använda ProxySQL för att åstadkomma det. Låt oss ta en titt på hur det kan göras.

Det finns två sätt att implementera vitlista i ProxySQL. Först, den historiska, skulle vara att skapa en catch-all-regel som blockerar alla frågor. Det bör vara den sista frågeregeln i kedjan. Ett exempel nedan:

Vi matchar varje sträng och genererar ett felmeddelande. Detta är den enda regeln som finns för närvarande, den förhindrar att någon fråga exekveras.

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.

Som du kan se kan vi inte köra några frågor. För att vår applikation ska fungera måste vi skapa frågeregler för alla frågor som vi vill tillåta att köra. Det kan göras per fråga, baserat på sammanfattningen eller mönstret. Du kan också tillåta trafik baserat på andra faktorer:användarnamn, klientvärd, schema. Låt oss tillåta SELECT till en av tabellerna:

Nu kan vi köra frågor på den här tabellen, men inte på någon annan:

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.

Problemet med detta tillvägagångssätt är att det inte hanteras effektivt i ProxySQL, därför kommer i ProxySQL 2.0.9 med en ny mekanism för brandvägg som inkluderar en ny algoritm, fokuserad på just detta användningsfall och som sådan mer effektiv. Låt oss se hur vi kan använda det.

Först måste vi installera ProxySQL 2.0.9. Du kan ladda ner paket manuellt från https://github.com/sysown/proxysql/releases/tag/v2.0.9 eller så kan du ställa in ProxySQL-förvaret.

När detta är gjort kan vi börja titta på det och försöka konfigurera det för att använda SQL-brandväggen.

Processen i sig är ganska enkel. Först och främst måste du lägga till en användare i tabellen mysql_firewall_whitelist_users. Den innehåller alla användare för vilka brandväggen ska vara aktiverad.

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)

I frågan ovan lade vi till "sbtest"-användare till listan över användare som borde ha brandvägg aktiverad. Det är möjligt att säga att endast anslutningar från en given värd testas mot brandväggsreglerna. Du kan också ha tre lägen:'AV', när brandväggen inte används, 'DETECTING', där felaktiga frågor loggas men inte blockeras och 'SKYDDA', där otillåtna frågor inte kommer att köras.

Låt oss aktivera vår brandvägg:

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-brandväggen baseras på sammanfattningen av frågorna, den tillåter inte att reguljära uttryck används. Det bästa sättet att samla in data om vilka frågor som bör tillåtas är att använda tabellen stats.stats_mysql_query_digest, där du kan samla in frågor och deras sammanfattningar. Utöver det kommer ProxySQL 2.0.9 med en ny tabell:history_mysql_query_digest, som är en beständig förlängning av den tidigare nämnda in-memory-tabellen. Du kan konfigurera ProxySQL för att lagra data på disk från tid till annan:

mysql> SET admin-stats_mysql_query_digest_to_disk=30;

Query OK, 1 row affected (0.00 sec)

Var 30:e sekund kommer data om frågor att lagras på disken. Låt oss se hur det går. Vi kommer att köra ett par frågor och sedan kontrollera deras sammanfattningar:

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)

När vi ställer in brandväggen på 'DETECTING'-läge ser vi även poster i loggen:

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]

Nu, om vi vill börja blockera frågor, bör vi uppdatera vår användare och ställa in läget på "SKYDDAR". Detta kommer att blockera all trafik så låt oss börja med att vitlista frågorna ovan. Sedan aktiverar vi läget "SKYDDA":

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)

Det är allt. Nu kan vi köra vitlistade frågor:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

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

| id   | k |

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

| 7615 | 1942 |

| 3355 | 2310 |

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

2 rows in set (0.00 sec)

Men vi kan inte köra icke-vitlistade:

mysql> SELECT id, k FROM sbtest3 LIMIT 2;

ERROR 1148 (42000): Firewall blocked this query

ProxySQL 2.0.9 kommer med ännu en intressant säkerhetsfunktion. Den har inbäddad libsqlinjection och du kan aktivera detektering av möjliga SQL-injektioner. Detektering baseras på algoritmerna från libsqlinjection. Den här funktionen kan aktiveras genom att köra:

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)

Det fungerar med brandväggen på följande sätt:

  • Om brandväggen är aktiverad och användaren är i PROTECTING-läge, används inte SQL-injektionsdetektering eftersom endast explicit vitlistade frågor kan passera.
  • Om brandväggen är aktiverad och användaren är i DETECTING-läge, testas inte vitlistade frågor för SQL-injektion, alla andra kommer att testas.
  • Om brandväggen är aktiverad och användaren är i 'AV'-läge, antas alla frågor vara vitlistade och ingen kommer att testas för SQL-injektion.
  • Om brandväggen är inaktiverad kommer alla frågor att testas för SQL-intektion.

I grund och botten används den bara om brandväggen är inaktiverad eller för användare i läget "DETECTING". SQL-injektionsdetektering kommer tyvärr med ganska många falska positiva resultat. Du kan använda tabellen mysql_firewall_whitelist_sqli_fingerprints för att vitlista fingeravtryck för frågor som har upptäckts felaktigt. Låt oss se hur det fungerar. Låt oss först inaktivera brandväggen:

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)

Låt oss sedan köra några frågor.

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

Det finns faktiskt falska positiva resultat. I loggen kunde vi hitta:

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, låt oss lägga till det här fingeravtrycket i vitlistan:

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 kan vi äntligen köra denna fråga:

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

| id   | k |

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

|   84 | 2456 |

| 6006 | 2588 |

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

2 rows in set (0.01 sec)

Vi försökte köra sysbench-arbetsbelastning, detta resulterade i att ytterligare två fingeravtryck lades till i vitlistastabellen:

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

Vi ville se om denna automatiska SQL-injektion kan skydda oss mot vår gode vän, 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)

Tyvärr inte riktigt. Kom ihåg att den här funktionen är baserad på automatiserade kriminaltekniska algoritmer, den är långt ifrån perfekt. Det kan komma som ett ytterligare försvarslager, men det kommer aldrig att kunna ersätta korrekt underhållen brandvägg som skapats av någon som känner till programmet och dess frågor.

Vi hoppas att du efter att ha läst denna korta, tvådelade serie har en bättre förståelse för hur du kan skydda din databas mot SQL-injektion och skadliga försök (eller helt enkelt användarfel) med ProxySQL. Om du har fler idéer vill vi gärna höra från dig i kommentarerna.


  1. Kombinera flera resultat i en underfråga till ett enda kommaseparerat värde

  2. Hur man distribuerar MariaDB Cluster 10.5 för hög tillgänglighet

  3. Hur man installerar och säkrar MariaDB 10 i CentOS 7

  4. Vanliga fel vid migrering av PostgreSQL-databaser från On-Prem till AWS RDS