संक्षिप्त उत्तर है हां, हां, mysql_real_escape_string()
के आसपास जाने का एक तरीका है .#बहुत अस्पष्ट किनारे के मामलों के लिए!!!
लंबा जवाब इतना आसान नहीं है। यह एक हमले पर आधारित है यहां प्रदर्शित ।
हमला
तो, आइए हमला दिखाते हुए शुरुआत करते हैं...
mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
कुछ परिस्थितियों में, वह 1 से अधिक पंक्ति लौटाएगा। आइए देखें कि यहां क्या हो रहा है:
-
चरित्र सेट का चयन करना
mysql_query('SET NAMES gbk');
इस हमले के काम करने के लिए, हमें उस एन्कोडिंग की आवश्यकता है जो सर्वर कनेक्शन पर दोनों को एन्कोड करने की अपेक्षा कर रहा है
'
जैसा कि ASCII यानी0x27
. में है और कुछ चरित्र रखने के लिए जिसका अंतिम बाइट ASCII है\
यानी0x5c
. जैसा कि यह पता चला है, डिफ़ॉल्ट रूप से MySQL 5.6 में समर्थित 5 ऐसे एन्कोडिंग हैं:big5
,cp932
,gb2312
,gbk
औरsjis
. हमgbk
. चुनेंगे यहाँ।अब,
SET NAMES
. के उपयोग पर ध्यान देना बहुत महत्वपूर्ण है यहाँ। यह वर्ण सेट सर्वर पर . सेट करता है . अगर हम C API फ़ंक्शन के लिए कॉल का उपयोग करते हैंmysql_set_charset()
, हम ठीक रहेंगे (2006 से MySQL रिलीज़ पर)। लेकिन एक मिनट में क्यों... -
पेलोड
इस इंजेक्शन के लिए हम जिस पेलोड का उपयोग करने जा रहे हैं, वह बाइट अनुक्रम से शुरू होता है
0xbf27
.gbk
. में , यह एक अमान्य मल्टीबाइट वर्ण है;latin1
. में , यह स्ट्रिंग है¿'
. ध्यान दें किlatin1
. में औरgbk
,0x27
अपने आप में एक शाब्दिक'
. है चरित्र।हमने इस पेलोड को चुना है, क्योंकि अगर हम
addslashes()
. कहते हैं उस पर, हम एक ASCII\
डालेंगे यानी0x5c
,'
. से पहले चरित्र। तो हम0xbf5c27
. के साथ समाप्त करेंगे , जोgbk
. में है दो वर्णों का क्रम है:0xbf5c
उसके बाद0x27
. या दूसरे शब्दों में, एक वैध कैरेक्टर के बाद अनस्केप्ड'
. लेकिन हमaddslashes()
. का उपयोग नहीं कर रहे हैं . तो अगले चरण पर... -
mysql_real_escape_string()
C API
mysql_real_escape_string()
पर कॉल करता हैaddslashes()
. से अलग है उसमें यह कनेक्शन वर्ण सेट जानता है। तो यह उस चरित्र सेट के लिए ठीक से भागने का प्रदर्शन कर सकता है जिसकी सर्वर अपेक्षा कर रहा है। हालांकि, इस बिंदु तक, क्लाइंट को लगता है कि हम अभी भीlatin1
. का उपयोग कर रहे हैं कनेक्शन के लिए, क्योंकि हमने इसे अन्यथा कभी नहीं बताया। हमने सर्वर . को बताया था हमgbk
का उपयोग कर रहे हैं , लेकिन क्लाइंट अब भी लगता है कि यहlatin1
है ।इसलिए
mysql_real_escape_string()
पर कॉल करें बैकस्लैश सम्मिलित करता है, और हमारे पास एक निःशुल्क हैंगिंग'
. है हमारे "बच निकले" सामग्री में चरित्र! वास्तव में, अगर हम$var
. को देखें तोgbk
. में वर्ण सेट, हम देखेंगे:縗' OR 1=1 /*
कौन सा है बिल्कुल वही हमले की आवश्यकता है।
-
प्रश्न
यह हिस्सा सिर्फ एक औपचारिकता है, लेकिन यहां दी गई क्वेरी है:
SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1
बधाई हो, आपने अभी हाल ही में mysql_real_escape_string()
. का उपयोग करके किसी प्रोग्राम पर सफलतापूर्वक हमला किया है ...
द बैड
ये खराब हो जाता है। PDO
डिफ़ॉल्ट रूप से अनुकरण MySQL के साथ तैयार बयान। इसका मतलब है कि क्लाइंट साइड पर, यह मूल रूप से mysql_real_escape_string()
के माध्यम से स्प्रिंटफ करता है। (सी लाइब्रेरी में), जिसका अर्थ है कि निम्नलिखित के परिणामस्वरूप एक सफल इंजेक्शन होगा:
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
अब, यह ध्यान देने योग्य है कि आप नकली तैयार बयानों को अक्षम करके इसे रोक सकते हैं:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
यह आमतौर पर होगा एक सही तैयार बयान में परिणाम (यानी क्वेरी से अलग पैकेट में डेटा भेजा जा रहा है)। हालांकि, ध्यान रखें कि पीडीओ चुपचाप फॉलबैक कर देगा। उन बयानों का अनुकरण करने के लिए जिन्हें MySQL मूल रूप से तैयार नहीं कर सकता:वे हैं जो यह कर सकते हैं सूचीबद्ध मैनुअल में, लेकिन उचित सर्वर संस्करण का चयन करने के लिए सावधान रहें)।
द अग्ली
मैंने शुरुआत में ही कहा था कि अगर हम mysql_set_charset('gbk')
का इस्तेमाल करते तो हम इन सब को रोक सकते थे। SET NAMES gbk
. के बजाय . और यह सच है बशर्ते आप 2006 से MySQL रिलीज़ का उपयोग कर रहे हों।
यदि आप किसी पुराने MySQL रिलीज़ का उपयोग कर रहे हैं, तो एक बग
में mysql_real_escape_string()
इसका मतलब है कि अमान्य मल्टीबाइट वर्ण जैसे कि हमारे पेलोड में से बचने के उद्देश्यों के लिए एकल बाइट्स के रूप में माना जाता था भले ही क्लाइंट को कनेक्शन एन्कोडिंग के बारे में सही ढंग से सूचित किया गया हो और इसलिए यह हमला अभी भी सफल होगा। बग को MySQL 4.1.20 में ठीक किया गया था
, 5.0.22 और 5.1.11 ।
लेकिन सबसे बुरी बात यह है कि PDO
mysql_set_charset()
. के लिए C API को उजागर नहीं किया 5.3.6 तक, इसलिए पिछले संस्करणों में यह नहीं हर संभव आदेश के लिए इस हमले को रोकें! अब यह एक के रूप में सामने आ गया है। डीएसएन पैरामीटर
।
द सेविंग ग्रेस
जैसा कि हमने शुरुआत में कहा था, इस हमले के काम करने के लिए डेटाबेस कनेक्शन को एक कमजोर वर्ण सेट का उपयोग करके एन्कोड किया जाना चाहिए। utf8mb4
असुरक्षित नहीं है और फिर भी हर . का समर्थन कर सकते हैं यूनिकोड वर्ण:ताकि आप इसके बजाय इसका उपयोग करने का चुनाव कर सकें- लेकिन यह केवल MySQL 5.5.3 के बाद से उपलब्ध है। एक विकल्प है utf8
, जो कमजोर नहीं . भी है और संपूर्ण यूनिकोड मूल बहुभाषी विमान
का समर्थन कर सकता है ।
वैकल्पिक रूप से, आप NO_BACKSLASH_ESCAPES
को सक्षम कर सकते हैं।
SQL मोड, जो (अन्य बातों के अलावा) mysql_real_escape_string()
के संचालन को बदल देता है . इस मोड के सक्षम होने पर, 0x27
0x2727
. से बदल दिया जाएगा के बजाय 0x5c27
और इस प्रकार बचने की प्रक्रिया नहीं किसी भी संवेदनशील एन्कोडिंग में मान्य वर्ण बनाएं जहां वे पहले मौजूद नहीं थे (यानी 0xbf27
अभी भी 0xbf27
है आदि) - तो सर्वर अभी भी स्ट्रिंग को अमान्य के रूप में अस्वीकार कर देगा। हालांकि, देखें @eggyal's answer
एक अलग भेद्यता के लिए जो इस SQL मोड का उपयोग करने से उत्पन्न हो सकती है।
सुरक्षित उदाहरण
निम्नलिखित उदाहरण सुरक्षित हैं:
mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
क्योंकि सर्वर अपेक्षित utf8
. है ...
mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
क्योंकि हमने कैरेक्टर सेट को ठीक से सेट किया है ताकि क्लाइंट और सर्वर मेल खा सकें।
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
क्योंकि हमने नकली तैयार स्टेटमेंट को बंद कर दिया है।
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
क्योंकि हमने कैरेक्टर सेट को ठीक से सेट किया है।
$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();
क्योंकि MySQLi हर समय तैयार स्टेटमेंट को सच करता है।
रैपिंग अप
अगर आप:
- MySQL के आधुनिक संस्करणों का उपयोग करें (देर से 5.1, सभी 5.5, 5.6, आदि) और
mysql_set_charset()
/$mysqli->set_charset()
/ पीडीओ का डीएसएन वर्णसेट पैरामीटर (PHP 5.3.6 में)
या
- कनेक्शन एन्कोडिंग के लिए संवेदनशील वर्ण सेट का उपयोग न करें (आप केवल
utf8
. का उपयोग करते हैं /latin1
/ascii
/ आदि)
आप 100% सुरक्षित हैं।
अन्यथा, आप असुरक्षित हैं भले ही आप mysql_real_escape_string()
का उपयोग कर रहे हों ...