इस ब्लॉग के पहले भाग में हमने बताया कि कैसे ProxySQL का उपयोग खतरनाक मानी जाने वाली आने वाली क्वेरी को ब्लॉक करने के लिए किया जा सकता है। जैसा कि आपने उस ब्लॉग में देखा, इसे हासिल करना बहुत आसान है। हालांकि यह पूर्ण समाधान नहीं है। हो सकता है कि आपको और भी अधिक सुरक्षित सेटअप तैयार करने की आवश्यकता हो - हो सकता है कि आप सभी प्रश्नों को ब्लॉक करना चाहें और फिर कुछ चुनिंदा प्रश्नों को पास करने की अनुमति दें। इसे पूरा करने के लिए ProxySQL का उपयोग करना संभव है। आइए देखें कि यह कैसे किया जा सकता है।
ProxySQL में श्वेतसूची को लागू करने के दो तरीके हैं। सबसे पहले, ऐतिहासिक एक, कैच-ऑल नियम बनाना होगा जो सभी प्रश्नों को अवरुद्ध कर देगा। यह श्रृंखला में अंतिम क्वेरी नियम होना चाहिए। नीचे एक उदाहरण:
हम प्रत्येक स्ट्रिंग का मिलान कर रहे हैं और एक त्रुटि संदेश उत्पन्न कर रहे हैं। इस समय मौजूद यही एकमात्र नियम है, यह किसी भी क्वेरी को निष्पादित होने से रोकता है।
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.
जैसा कि आप देख सकते हैं, हम कोई क्वेरी नहीं चला सकते। हमारे आवेदन के काम करने के लिए हमें उन सभी प्रश्नों के लिए क्वेरी नियम बनाने होंगे जिन्हें हम निष्पादित करने की अनुमति देना चाहते हैं। यह पाचन या पैटर्न के आधार पर प्रति प्रश्न किया जा सकता है। आप अन्य कारकों के आधार पर भी ट्रैफ़िक की अनुमति दे सकते हैं:उपयोगकर्ता नाम, क्लाइंट होस्ट, स्कीमा। आइए किसी एक तालिका में SELECTs को अनुमति दें:
अब हम इस तालिका पर प्रश्नों को निष्पादित कर सकते हैं, लेकिन किसी अन्य पर नहीं:
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.
इस दृष्टिकोण के साथ समस्या यह है कि इसे ProxySQL में कुशलता से नियंत्रित नहीं किया जाता है, इसलिए ProxySQL 2.0.9 में फ़ायरवॉलिंग के नए तंत्र के साथ आता है जिसमें नया एल्गोरिदम शामिल है, जो इस विशेष उपयोग के मामले पर केंद्रित है और इस तरह के और अधिक कुशल। आइए देखें कि हम इसका उपयोग कैसे कर सकते हैं।
सबसे पहले, हमें ProxySQL 2.0.9 इंस्टॉल करना होगा। आप https://github.com/sysown/proxysql/releases/tag/v2.0.9 से मैन्युअल रूप से पैकेज डाउनलोड कर सकते हैं या आप ProxySQL रिपॉजिटरी सेट कर सकते हैं।
एक बार यह हो जाने के बाद, हम इसे देखना शुरू कर सकते हैं और SQL फ़ायरवॉल का उपयोग करने के लिए इसे कॉन्फ़िगर करने का प्रयास कर सकते हैं।
प्रक्रिया अपने आप में काफी आसान है। सबसे पहले, आपको एक उपयोगकर्ता को mysql_firewall_whitelist_users तालिका में जोड़ना होगा। इसमें वे सभी उपयोगकर्ता शामिल हैं जिनके लिए फ़ायरवॉल सक्षम किया जाना चाहिए।
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)
उपरोक्त क्वेरी में हमने 'sbtest' उपयोगकर्ता को उन उपयोगकर्ताओं की सूची में जोड़ा है जिनके पास फ़ायरवॉल सक्षम होना चाहिए। यह बताना संभव है कि किसी दिए गए होस्ट से केवल कनेक्शन का फ़ायरवॉल नियमों के विरुद्ध परीक्षण किया जाता है। आपके पास तीन मोड भी हो सकते हैं:'ऑफ', जब फ़ायरवॉल का उपयोग नहीं किया जाता है, 'डिटेक्टिंग', जहां गलत क्वेरी लॉग की जाती हैं, लेकिन ब्लॉक नहीं की जाती हैं और 'प्रोटेक्टिंग', जहां अनुमति नहीं है, वहां क्वेरी को निष्पादित नहीं किया जाएगा।
आइए अपना फ़ायरवॉल सक्षम करें:
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)
प्रॉक्सीएसक्यूएल फ़ायरवॉल प्रश्नों के डाइजेस्ट पर आधारित है, यह रेगुलर एक्सप्रेशन के उपयोग की अनुमति नहीं देता है। किस क्वेरी की अनुमति दी जानी चाहिए, इसके बारे में डेटा एकत्र करने का सबसे अच्छा तरीका stats.stats_mysql_query_digest तालिका का उपयोग करना है, जहां आप प्रश्न और उनके डाइजेस्ट एकत्र कर सकते हैं। उसके ऊपर, ProxySQL 2.0.9 एक नई तालिका के साथ आता है:history_mysql_query_digest, जो पहले बताई गई इन-मेमोरी तालिका का एक निरंतर विस्तार है। आप समय-समय पर डिस्क पर डेटा स्टोर करने के लिए ProxySQL को कॉन्फ़िगर कर सकते हैं:
mysql> SET admin-stats_mysql_query_digest_to_disk=30;
Query OK, 1 row affected (0.00 sec)
प्रत्येक 30 सेकंड में प्रश्नों के बारे में डेटा डिस्क पर संग्रहीत किया जाएगा। चलो यह कैसे जाता है देखते हैं। हम कुछ प्रश्नों को निष्पादित करेंगे और फिर उनके डाइजेस्ट की जांच करेंगे:
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)
जैसे ही हम फ़ायरवॉल को 'डिटेक्टिंग' मोड पर सेट करते हैं, हम लॉग में प्रविष्टियाँ भी देखेंगे:
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]
अब, यदि हम प्रश्नों को अवरुद्ध करना शुरू करना चाहते हैं, तो हमें अपने उपयोगकर्ता को अपडेट करना चाहिए और मोड को 'प्रोटेक्टिंग' पर सेट करना चाहिए। यह सभी ट्रैफ़िक को अवरुद्ध कर देगा, तो चलिए उपरोक्त प्रश्नों को श्वेतसूची में डालकर शुरू करते हैं। फिर हम 'प्रोटेक्टिंग' मोड को इनेबल कर देंगे:
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)
बस। अब हम श्वेतसूचीबद्ध प्रश्नों को निष्पादित कर सकते हैं:
mysql> SELECT id, k FROM sbtest1 LIMIT 2;
+------+------+
| id | k |
+------+------+
| 7615 | 1942 |
| 3355 | 2310 |
+------+------+
2 rows in set (0.00 sec)
लेकिन हम गैर-श्वेतसूचीबद्ध लोगों को निष्पादित नहीं कर सकते:
mysql> SELECT id, k FROM sbtest3 LIMIT 2;
ERROR 1148 (42000): Firewall blocked this query
ProxySQL 2.0.9 एक और दिलचस्प सुरक्षा विशेषता के साथ आता है। इसमें एम्बेडेड libsqlinjection है और आप संभावित SQL इंजेक्शन का पता लगाने में सक्षम कर सकते हैं। डिटेक्शन libsqlinjection के एल्गोरिदम पर आधारित है। इस सुविधा को चलाकर सक्षम किया जा सकता है:
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)
यह फ़ायरवॉल के साथ निम्न प्रकार से कार्य करता है:
- यदि फ़ायरवॉल सक्षम है और उपयोगकर्ता प्रोटेक्टिंग मोड में है, तो SQL इंजेक्शन डिटेक्शन का उपयोग नहीं किया जाता है क्योंकि केवल स्पष्ट रूप से श्वेतसूचीबद्ध क्वेरी ही पास हो सकती हैं।
- यदि फ़ायरवॉल सक्षम है और उपयोगकर्ता डिटेक्टिंग मोड में है, तो श्वेतसूचीबद्ध क्वेरीज़ का SQL इंजेक्शन के लिए परीक्षण नहीं किया जाता है, अन्य सभी का परीक्षण किया जाएगा।
- यदि फ़ायरवॉल सक्षम है और उपयोगकर्ता 'ऑफ़' मोड में है, तो सभी प्रश्नों को श्वेतसूची में माना जाएगा और SQL इंजेक्शन के लिए किसी का भी परीक्षण नहीं किया जाएगा।
- यदि फ़ायरवॉल अक्षम है, तो सभी क्वेरीज़ का SQL इंटेक्शन के लिए परीक्षण किया जाएगा।
मूल रूप से, इसका उपयोग केवल तभी किया जाता है जब फ़ायरवॉल अक्षम हो या उपयोगकर्ताओं के लिए 'डिटेक्टिंग' मोड में हो। दुर्भाग्य से, SQL इंजेक्शन का पता लगाने में बहुत सारी झूठी सकारात्मकताएँ आती हैं। आप उन प्रश्नों के लिए फ़िंगरप्रिंट को श्वेतसूची में रखने के लिए तालिका mysql_firewall_whitelist_sqli_fingerprints का उपयोग कर सकते हैं जो गलत तरीके से पाए गए थे। आइए देखें कि यह कैसे काम करता है। सबसे पहले, आइए फ़ायरवॉल को अक्षम करें:
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)
फिर, कुछ प्रश्नों को चलाते हैं।
mysql> SELECT id, k FROM sbtest2 LIMIT 2;
ERROR 2013 (HY000): Lost connection to MySQL server during query
वास्तव में, झूठी सकारात्मकताएं हैं। लॉग में हम पा सकते हैं:
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
ठीक है, आइए इस फ़िंगरप्रिंट को श्वेतसूची तालिका में जोड़ें:
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)
अब हम अंततः इस क्वेरी को निष्पादित कर सकते हैं:
mysql> SELECT id, k FROM sbtest2 LIMIT 2;
+------+------+
| id | k |
+------+------+
| 84 | 2456 |
| 6006 | 2588 |
+------+------+
2 rows in set (0.01 sec)
हमने sysbench वर्कलोड चलाने की कोशिश की, इसके परिणामस्वरूप श्वेतसूची तालिका में दो और फ़िंगरप्रिंट जोड़े गए:
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
हम यह देखना चाहते थे कि क्या यह स्वचालित SQL इंजेक्शन हमारे अच्छे दोस्त, 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)
दुर्भाग्य से, वास्तव में नहीं। कृपया ध्यान रखें कि यह सुविधा स्वचालित फोरेंसिक एल्गोरिदम पर आधारित है, यह बिल्कुल सही नहीं है। यह रक्षा की एक अतिरिक्त परत के रूप में आ सकता है लेकिन यह कभी भी किसी ऐसे व्यक्ति द्वारा बनाए गए फ़ायरवॉल को ठीक से बनाए रखने में सक्षम नहीं होगा जो एप्लिकेशन और उसके प्रश्नों को जानता हो।
हम आशा करते हैं कि इस छोटी, दो-भाग वाली श्रृंखला को पढ़ने के बाद आपको इस बात की बेहतर समझ होगी कि आप अपने डेटाबेस को SQL इंजेक्शन और दुर्भावनापूर्ण प्रयासों (या केवल स्पष्ट रूप से उपयोगकर्ता त्रुटियों) के विरुद्ध ProxySQL का उपयोग करके कैसे सुरक्षित कर सकते हैं। यदि आपके पास और विचार हैं, तो हमें टिप्पणियों में आपसे सुनना अच्छा लगेगा।