MariaDB
 sql >> Database >  >> RDS >> MariaDB

Come proteggere il database MySQL o MariaDB da SQL injection:seconda parte

Nella prima parte di questo blog abbiamo descritto come utilizzare ProxySQL per bloccare le query in entrata ritenute pericolose. Come hai visto in quel blog, raggiungere questo obiettivo è molto facile. Questa non è una soluzione completa, però. Potrebbe essere necessario progettare una configurazione ancora più sicura:potresti voler bloccare tutte le query e quindi consentire il passaggio solo di alcune selezionate. È possibile utilizzare ProxySQL per farlo. Diamo un'occhiata a come si può fare.

Ci sono due modi per implementare la whitelist in ProxySQL. In primo luogo, quello storico, sarebbe quello di creare una regola generale che bloccherà tutte le query. Dovrebbe essere l'ultima regola di query nella catena. Un esempio qui sotto:

Stiamo abbinando ogni stringa e generiamo un messaggio di errore. Questa è l'unica regola esistente in questo momento, impedisce l'esecuzione di qualsiasi query.

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.

Come puoi vedere, non possiamo eseguire query. Affinché la nostra applicazione funzioni, dovremmo creare regole di query per tutte le query che vogliamo consentire di eseguire. Può essere eseguito per query, in base al digest o al pattern. Puoi anche consentire il traffico in base ad altri fattori:nome utente, host client, schema. Consentiamo SELECT a una delle tabelle:

Ora possiamo eseguire query su questa tabella, ma non su altre:

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.

Il problema con questo approccio è che non viene gestito in modo efficiente in ProxySQL, quindi in ProxySQL 2.0.9 viene fornito con un nuovo meccanismo di firewall che include un nuovo algoritmo, focalizzato su questo caso d'uso particolare e come tale più efficiente. Vediamo come possiamo usarlo.

Per prima cosa, dobbiamo installare ProxySQL 2.0.9. Puoi scaricare i pacchetti manualmente da https://github.com/sysown/proxysql/releases/tag/v2.0.9 oppure puoi configurare il repository ProxySQL.

Una volta fatto, possiamo iniziare a esaminarlo e provare a configurarlo per utilizzare il firewall SQL.

Il processo in sé è abbastanza semplice. Prima di tutto, devi aggiungere un utente alla tabella mysql_firewall_whitelist_users. Contiene tutti gli utenti per i quali deve essere abilitato il firewall.

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)

Nella query sopra abbiamo aggiunto l'utente "sbtest" all'elenco di utenti che dovrebbero avere il firewall abilitato. È possibile dire che solo le connessioni da un determinato host vengono testate rispetto alle regole del firewall. Puoi anche avere tre modalità:"OFF", quando il firewall non viene utilizzato, "RILEVAMENTO", in cui le query errate vengono registrate ma non bloccate e "PROTEZIONE", in cui le query non consentite non verranno eseguite.

Abilitiamo il nostro 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)

Il firewall proxySQL si basa sul digest delle query, non consente l'uso di espressioni regolari. Il modo migliore per raccogliere dati su quali query dovrebbero essere consentite è utilizzare la tabella stats.stats_mysql_query_digest, dove puoi raccogliere le query e i relativi riassunti. Inoltre, ProxySQL 2.0.9 viene fornito con una nuova tabella:history_mysql_query_digest, che è un'estensione persistente della tabella in memoria menzionata in precedenza. È possibile configurare ProxySQL per archiviare i dati su disco di volta in volta:

mysql> SET admin-stats_mysql_query_digest_to_disk=30;

Query OK, 1 row affected (0.00 sec)

Ogni 30 secondi i dati sulle query verranno archiviati su disco. Vediamo come va. Eseguiremo un paio di query e quindi controlleremo i loro riassunti:

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)

Poiché impostiamo il firewall in modalità "RILEVAMENTO", vedremo anche le voci nel registro:

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]

Ora, se vogliamo iniziare a bloccare le query, dovremmo aggiornare il nostro utente e impostare la modalità su "PROTEZIONE". Questo bloccherà tutto il traffico, quindi iniziamo inserendo le query nella whitelist sopra. Quindi attiveremo la modalità "PROTEZIONE":

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)

Ecco fatto. Ora possiamo eseguire query autorizzate:

mysql> SELECT id, k FROM sbtest1 LIMIT 2;

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

| id   | k |

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

| 7615 | 1942 |

| 3355 | 2310 |

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

2 rows in set (0.00 sec)

Ma non possiamo eseguire quelli non autorizzati:

mysql> SELECT id, k FROM sbtest3 LIMIT 2;

ERROR 1148 (42000): Firewall blocked this query

ProxySQL 2.0.9 include un'altra interessante funzionalità di sicurezza. Ha incorporato libsqlinjection ed è possibile abilitare il rilevamento di possibili iniezioni SQL. Il rilevamento si basa sugli algoritmi di libsqlinjection. Questa funzione può essere abilitata eseguendo:

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)

Funziona con il firewall nel modo seguente:

  • Se il firewall è abilitato e l'utente è in modalità PROTECTING, il rilevamento SQL injection non viene utilizzato poiché solo le query esplicitamente autorizzate possono passare.
  • Se il firewall è abilitato e l'utente è in modalità RILEVAMENTO, le query autorizzate non vengono testate per SQL injection, tutte le altre verranno testate.
  • Se il firewall è abilitato e l'utente è in modalità "OFF", si presume che tutte le query siano inserite nella whitelist e nessuna verrà testata per SQL injection.
  • Se il firewall è disabilitato, tutte le query verranno testate per l'interazione SQL.

In sostanza, viene utilizzato solo se il firewall è disabilitato o per utenti in modalità 'RILEVAMENTO'. Il rilevamento dell'iniezione SQL, sfortunatamente, presenta molti falsi positivi. È possibile utilizzare la tabella mysql_firewall_whitelist_sqli_fingerprints per inserire nella whitelist le impronte digitali per le query rilevate in modo errato. Vediamo come funziona. Per prima cosa, disabilitiamo il 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)

Allora, eseguiamo alcune query.

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

In effetti, ci sono falsi positivi. Nel log potremmo trovare:

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, aggiungiamo questa impronta digitale alla tabella della whitelist:

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)

Ora possiamo finalmente eseguire questa query:

mysql> SELECT id, k FROM sbtest2 LIMIT 2;

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

| id   | k |

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

|   84 | 2456 |

| 6006 | 2588 |

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

2 rows in set (0.01 sec)

Abbiamo provato a eseguire il carico di lavoro di sysbench, questo ha comportato l'aggiunta di altre due impronte digitali alla tabella della 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

Volevamo vedere se questa iniezione SQL automatizzata può proteggerci dal nostro buon amico, 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)

Purtroppo non proprio. Tieni presente che questa funzione si basa su algoritmi forensi automatizzati, è tutt'altro che perfetta. Potrebbe rappresentare un ulteriore livello di difesa, ma non sarà mai in grado di sostituire un firewall adeguatamente mantenuto creato da qualcuno che conosce l'applicazione e le sue query.

Ci auguriamo che dopo aver letto questa breve serie in due parti tu abbia una migliore comprensione di come proteggere il tuo database da SQL injection e tentativi dannosi (o semplicemente errori dell'utente) utilizzando ProxySQL. Se hai altre idee, ci piacerebbe sentirti nei commenti.