SQL इंजेक्शन जो mysql_real_escape_string () के आसपास मिलता है


645

mysql_real_escape_string()फ़ंक्शन का उपयोग करते समय क्या कोई SQL इंजेक्शन संभावना भी है ?

इस नमूना स्थिति पर विचार करें। SQL का निर्माण PHP में इस प्रकार किया जाता है:

$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

मैंने सुना है कि कई लोग मुझसे कहते हैं कि कोड अभी भी खतरनाक है और mysql_real_escape_string()उपयोग किए गए फ़ंक्शन के साथ भी हैक करना संभव है । लेकिन मैं किसी भी संभावित शोषण के बारे में नहीं सोच सकता?

इस तरह के क्लासिक इंजेक्शन:

aaa' OR 1=1 --

कम मत करो।

क्या आप ऊपर दिए गए PHP कोड के माध्यम से मिलने वाले किसी भी संभावित इंजेक्शन के बारे में जानते हैं?


34
@ThiefMaster - मैं अमान्य उपयोगकर्ता / अमान्य पासवर्ड जैसी गलतियाँ नहीं देना पसंद करता हूं ... यह क्रूर बल व्यापारियों को बताता है कि उनके पास एक वैध उपयोगकर्ता आईडी है, और यह सिर्फ पासवर्ड है जो उन्हें अनुमान लगाने की आवश्यकता है
मार्क बेकर

18
हालांकि यह एक प्रयोज्य दृष्टिकोण से भयानक है। कभी-कभी आप अपने मुख्य उपनाम / उपयोगकर्ता नाम / ईमेल-पते का उपयोग नहीं कर सकते हैं और कुछ समय के बाद इसे भूल जाते हैं या साइट निष्क्रियता के लिए आपके खाते को हटा देती है। यदि आप पासवर्ड की कोशिश जारी रखते हैं और तब भी आपका आईपी अवरुद्ध हो जाता है तो भी यह बहुत कष्टप्रद है, भले ही यह आपका उपयोगकर्ता नाम अमान्य हो।
ThiefMaster

50
कृपया, mysql_*नए कोड में कार्यों का उपयोग न करें । अब उनका रखरखाव नहीं किया गया है और इस पर वनीकरण प्रक्रिया शुरू हो गई है। लाल बॉक्स देखें? इसके बजाय तैयार किए गए कथनों के बारे में जानें, और PDO या MySQLi का उपयोग करें- यह लेख आपको निर्णय लेने में मदद करेगा। यदि आप पीडीओ चुनते हैं, तो यहां एक अच्छा ट्यूटोरियल है
tereško

13
@machineaddict, 5.5 के बाद से (जो हाल ही में जारी किया गया था) mysql_*फ़ंक्शन पहले से ही E_DEPRECATEDचेतावनी का उत्पादन करते हैं । ext/mysqlविस्तार से अधिक 10 साल के लिए नहीं रखा गया है। क्या आप वास्तव में इतने भ्रमित हैं?
tereško

13
@machineaddict उन्होंने PHP 7.0 पर अभी उस एक्सटेंशन को हटा दिया है और यह अभी 2050 नहीं है।
GGG

जवाबों:


379

निम्नलिखित प्रश्न पर विचार करें:

$iId = mysql_real_escape_string("1 OR 1=1");    
$sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string()इसके खिलाफ आपकी रक्षा नहीं करेगा। तथ्य यह है कि आप ' 'अपनी क्वेरी के अंदर अपने चर के आसपास सिंगल कोट्स ( ) का उपयोग करते हैं, जो आपको इस से बचाता है। निम्नलिखित भी एक विकल्प है:

$iId = (int)"1 OR 1=1";
$sSql = "SELECT * FROM table WHERE id = $iId";

9
लेकिन यह एक वास्तविक समस्या नहीं होगी, क्योंकि mysql_query()कई बयानों को निष्पादित नहीं करता है, नहीं?
पेका

11
@Pekka, हालांकि सामान्य उदाहरण है DROP TABLE, व्यवहार में हमलावर की अधिक संभावना है SELECT passwd FROM users। बाद के मामले में, दूसरी क्वेरी आमतौर पर एक UNIONखंड के उपयोग द्वारा निष्पादित की जाती है ।
जैको

58
(int)mysql_real_escape_string- इसका कोई अर्थ नहीं निकलता। यह बिल्कुल अलग नहीं है (int)। और वे हर इनपुट के लिए एक ही परिणाम का उत्पादन करेंगे
जूल

28
यह किसी भी चीज़ की तुलना में फ़ंक्शन का दुरुपयोग है। आखिर, यह नाम है mysql_real_escape_string, नहीं mysql_real_escape_integer। यह पूर्णांक फ़ील्ड के साथ उपयोग करने के लिए नहीं है।
NullUserException

11
@ircmaxell, फिर भी जवाब पूरी तरह से भ्रामक है। जाहिर है प्रश्न उद्धरण के भीतर सामग्री के बारे में पूछ रहा है । "उद्धरण नहीं हैं" इस प्रश्न का उत्तर नहीं है।
पचेरियर

629

संक्षिप्त उत्तर हाँ है, हाँ वहाँ एक रास्ता है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 से अधिक पंक्ति में लौटेगा। चलो यहाँ क्या चल रहा है:

  1. कैरेक्टर सेट का चयन करना

    mysql_query('SET NAMES gbk');

    काम करने के लिए इस हमले के लिए, हम एन्कोडिंग की जरूरत है कि सर्वर के दोनों एनकोड करने के लिए कनेक्शन पर उम्मीद 'ASCII यानी के रूप में 0x27 और कुछ चरित्र जिसका अंतिम बाइट एक ASCII है के लिए \यानी 0x5c। यह पता चला है के रूप में, 5 ऐसे डिफ़ॉल्ट रूप से MySQL 5.6 में समर्थित एनकोडिंग हैं: big5, cp932, gb2312, gbkऔर sjis। हम gbkयहाँ का चयन करेंगे ।

    अब, SET NAMESयहां के उपयोग पर ध्यान देना बहुत महत्वपूर्ण है। यह सर्वर पर सेट चरित्र सेट करता है । यदि हमने C API फ़ंक्शन को कॉल का उपयोग किया है mysql_set_charset(), तो हम ठीक होंगे (2006 से MySQL के रिलीज़ पर)। लेकिन एक मिनट में ही क्यों ...

  2. पेलोड

    इस इंजेक्शन के लिए हम जिस पेलोड का उपयोग करने जा रहे हैं, वह बाइट अनुक्रम से शुरू होता है 0xbf27। में gbk, यह एक अमान्य मल्टीबाइट चरित्र है; में latin1, यह स्ट्रिंग है ¿'। ध्यान दें कि में latin1 और gbk , 0x27पर अपने आप ही एक शाब्दिक है 'चरित्र।

    हमने इस पेलोड को चुना है, क्योंकि अगर हमने इस addslashes()पर कॉल किया , तो हम चरित्र से पहले एक ASCII \यानी सम्मिलित करेंगे । तो हम एक साथ एक दो चरित्र अनुक्रम है , जिसके साथ हवा होगी : द्वारा पीछा किया । या दूसरे शब्दों में, एक वैध चरित्र जिसके बाद एक गैर-पंजीकृत व्यक्ति आता है । लेकिन हम उपयोग नहीं कर रहे हैं । तो अगले कदम पर ...0x5c'0xbf5c27gbk0xbf5c0x27'addslashes()

  3. mysql_real_escape_string ()

    सी एपीआई कॉल mysql_real_escape_string()इससे अलग addslashes()है कि यह कनेक्शन वर्ण सेट को जानता है। तो यह सर्वर सेट की उम्मीद कर रहे चरित्र सेट के लिए भागने का ठीक से प्रदर्शन कर सकता है। हालाँकि, इस बिंदु तक, क्लाइंट को लगता है कि हम अभी भी latin1कनेक्शन के लिए उपयोग कर रहे हैं , क्योंकि हमने इसे अन्यथा कभी नहीं बताया। हमने उस सर्वर को बताया जिसका हम उपयोग कर रहे हैं gbk, लेकिन ग्राहक अभी भी सोचता है latin1

    इसलिए कॉल mysql_real_escape_string()बैकस्लैश सम्मिलित करता है, और 'हमारी "बच गई" सामग्री में एक मुफ़्त लटका हुआ चरित्र है! वास्तव में, अगर हमें चरित्र सेट $varमें देखना था gbk, तो हम देखेंगे:

    = 'या 1 = 1 / *

    जो वास्तव में हमले की आवश्यकता है।

  4. पूछताछ

    यह हिस्सा सिर्फ एक औपचारिकता है, लेकिन यहां प्रस्तुत किया गया प्रश्न है:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

बधाई हो, आपने अभी-अभी एक कार्यक्रम का सफलतापूर्वक उपयोग किया mysql_real_escape_string()...

खराब

ये और ख़राब हो जाता है। MySQL के साथ तैयार बयानों PDOका अनुकरण करने में चूक । इसका मतलब है कि क्लाइंट की तरफ, यह मूल रूप से 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()मतलब है कि इस तरह के हमारे पेलोड में उन लोगों के रूप में अवैध multibyte वर्ण प्रयोजनों से बचने के लिए एक बाइट के रूप में इलाज किया गया है, भले ही ग्राहक को सही ढंग से कनेक्शन एन्कोडिंग के बारे में सूचित कर दिया गया था और इसलिए इस हमले होगा अभी भी सफल हैं। बग MySQL 4.1.20 , 5.0.22 और 5.1.11 में तय किया गया था ।

लेकिन सबसे बुरी बात यह है कि 5.3.6 तक PDOसी एपीआई को उजागर नहीं किया गया mysql_set_charset(), इसलिए पूर्व संस्करणों में यह हर संभव आदेश के लिए इस हमले को रोक नहीं सकता है! यह अब DSN पैरामीटर के रूप में सामने आया है ।

द सेविंग ग्रेस

जैसा कि हमने शुरू में कहा, इस हमले के लिए डेटाबेस कनेक्शन को काम करने के लिए एक कमजोर चरित्र सेट का उपयोग करके एन्कोड किया जाना चाहिए। utf8mb4है न कमजोर अभी तक समर्थन कर सकते हैं और हर ताकि आप MySQL 5.5.3 के बाद से है कि उपलब्ध बजाय-लेकिन यह केवल किया गया है का उपयोग करने का चयन कर सकते हैं: यूनिकोड वर्ण। एक विकल्प है utf8, जो भी असुरक्षित नहीं है और पूरे यूनिकोड बेसिक बहुभाषी विमान का समर्थन कर सकता है ।

वैकल्पिक रूप से, आप NO_BACKSLASH_ESCAPESSQL मोड को सक्षम कर सकते हैं , जो (अन्य चीजों के बीच) के संचालन को बदल देता है mysql_real_escape_string()। सक्षम किए गए इस मोड के साथ, 0x27इसके 0x2727बजाय प्रतिस्थापित किया जाएगा 0x5c27और इस प्रकार भागने की प्रक्रिया किसी भी असुरक्षित एन्कोडिंग में मान्य वर्ण नहीं बना सकती है जहां वे पहले मौजूद नहीं थे (यानी 0xbf27अभी भी 0xbf27आदि।) - इसलिए सर्वर अभी भी स्ट्रिंग को अस्वीकार कर देगा। । हालाँकि, एक अलग भेद्यता के लिए @ eggyal का उत्तर देखें जो इस 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()/ PDO के DSN चारसेट पैरामीटर (PHP set 5.3.6 में) का उपयोग करें

या

  • कनेक्शन एन्कोडिंग के लिए एक कमजोर वर्ण सेट का उपयोग न करें (आप केवल utf8/ latin1/ ascii/ etc का उपयोग करें )

आप 100% सुरक्षित हैं।

अन्यथा, आप उपयोग करने के बावजूदmysql_real_escape_string() कमजोर हैं ...


3
MySQL के लिए तैयार पीडीओ का अनुकरण करते हुए, वास्तव में? मुझे कोई कारण नहीं दिखता कि ऐसा क्यों किया जाए क्योंकि ड्राइवर मूल रूप से इसका समर्थन करता है। नहीं?
netcoder

16
ऐसा होता है। वे कहते हैं कि यह दस्तावेज में नहीं है। लेकिन स्रोत कोड में, यह स्पष्ट रूप से दिखाई देता है और इसे ठीक करना आसान है। मैंने इसे देवताओं की अक्षमता तक बताया।
थियोडोर आर। स्मिथ

5
@ TheodoreR.Smith: यह तय करना इतना आसान नहीं है। मैं डिफ़ॉल्ट बदलने पर काम कर रहा हूं, लेकिन स्विच किए जाने पर यह परीक्षणों का एक नाव लोड विफल करता है। इसलिए ऐसा लगता है कि यह एक बड़ा बदलाव है। मैं अभी भी इसे 5.5 तक समाप्त होने की उम्मीद कर रहा हूं ...
ircmaxell

14
@ साध्य: नहीं, वर्णित लेख की भेद्यता के बारे में था addslashes। मैंने उस पर इस भेद्यता को आधारित किया । इसे स्वयं आज़माएं। जाओ MySQL 5.0, और इस शोषण को चलाने के लिए और अपने आप को देखें। जहाँ तक कि इसे PUT / GET / POST में डालने की बात है, यह TRIVIAL है। इनपुट डेटा केवल बाइट स्ट्रीम हैं। char(0xBF)बाइट जनरेट करने का एक पठनीय तरीका है। मैंने कई सम्मेलनों के सामने इस भेद्यता को लाइव प्रदर्शित किया है। इस पर मेरा विश्वास करो ... लेकिन अगर तुम नहीं, यह अपने आप कोशिश करो। यह काम करता है ...
ircmaxell

5
@shadyyx: ?var=%BF%27+OR+1=1+%2F%2AURL में $ _GET ... , $var = $_GET['var'];कोड में, और बॉब के अपने चाचा के रूप में इस तरह के फंकनेस को पारित करने के लिए ।
cHao

183

टी एल; डॉ

mysql_real_escape_string()यदि कोई सुरक्षा प्रदान नहीं करता है (और इसके अलावा आपके डेटा को कम कर सकता है):

  • MySQL का NO_BACKSLASH_ESCAPESSQL मोड सक्षम है (जो कि ऐसा हो सकता है, जब तक कि आप स्पष्ट रूप से कनेक्ट होने पर हर बार किसी अन्य SQL मोड का चयन न करें ); तथा

  • आपके SQL स्ट्रिंग शाब्दिक दोहरे-उद्धरण "वर्णों का उपयोग करके उद्धृत किए जाते हैं ।

इसे बग # 72458 के रूप में दायर किया गया था और इसे MySQL v5.7.6 (" सेविंग ग्रेस " के नीचे स्थित अनुभाग देखें) में तय किया गया है ।

यह एक और है, (शायद कम है?) अस्पष्ट मामला !!!

@ Ircmaxell के शानदार जवाब के लिए श्रद्धांजलि (वास्तव में, यह चापलूसी माना जाता है और साहित्यिक चोरी नहीं है!), मैं उनके प्रारूप को अपनाऊंगा :

हमला

एक प्रदर्शन के साथ शुरू ...

mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- ');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

यह testतालिका से सभी रिकॉर्ड लौटाएगा । एक विच्छेदन:

  1. SQL मोड का चयन करना

    mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');

    स्ट्रिंग साहित्य के तहत प्रलेखित :

    एक स्ट्रिंग के भीतर उद्धरण वर्णों को शामिल करने के कई तरीके हैं:

    • " '" के साथ उद्धृत स्ट्रिंग के अंदर एक " '" के रूप में लिखा जा सकता है ''

    • " "" के साथ उद्धृत स्ट्रिंग के अंदर एक " "" के रूप में लिखा जा सकता है ""

    • बच चरित्र (" \") द्वारा बोली चरित्र को प्राथमिकता दें ।

    • " '" के साथ उद्धृत स्ट्रिंग के अंदर " "" को किसी विशेष उपचार की आवश्यकता नहीं है और इसे दोगुना या बच जाने की आवश्यकता नहीं है। उसी तरह, " "" के साथ उद्धृत एक स्ट्रिंग के अंदर " '" को किसी विशेष उपचार की आवश्यकता नहीं है।

    यदि सर्वर के एसक्यूएल मोड में शामिल है NO_BACKSLASH_ESCAPES, तो इनमें से तीसरा विकल्प-जो सामान्य दृष्टिकोण है जिसे अपनाया गया है mysql_real_escape_string()- जो उपलब्ध नहीं है: इसके बजाय पहले दो विकल्पों में से एक का उपयोग किया जाना चाहिए। ध्यान दें कि चौथी गोली का प्रभाव यह है कि किसी व्यक्ति को आवश्यक रूप से उस चरित्र को जानना चाहिए जिसका उपयोग शाब्दिक उद्धरण के लिए किया जाएगा ताकि किसी के डेटा से बचने के लिए।

  2. पेलोड

    " OR 1=1 -- 

    पेलोड इस इंजेक्शन को "अक्षर के साथ सचमुच में शुरू करता है। कोई विशेष एन्कोडिंग नहीं। कोई विशेष पात्र नहीं। कोई अजीब बाइट्स नहीं।

  3. mysql_real_escape_string ()

    $var = mysql_real_escape_string('" OR 1=1 -- ');

    सौभाग्य से, mysql_real_escape_string()SQL मोड की जाँच करता है और उसके अनुसार अपने व्यवहार को समायोजित करता है। देखें libmysql.c:

    ulong STDCALL
    mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
                 ulong length)
    {
      if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
        return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
      return escape_string_for_mysql(mysql->charset, to, 0, from, length);
    }

    escape_quotes_for_mysql()यदि NO_BACKSLASH_ESCAPESSQL मोड उपयोग में है, तो इस प्रकार एक अलग अंतर्निहित फ़ंक्शन को लागू किया जाता है। जैसा कि ऊपर उल्लेख किया गया है, इस तरह के एक फ़ंक्शन को यह जानने की जरूरत है कि शाब्दिक रूप से दोहराए जाने वाले अन्य कोटेशन चरित्र को दोहराए बिना इसे दोहराने के लिए किस वर्ण का उपयोग किया जाएगा।

    हालांकि, यह फ़ंक्शन मनमाने ढंग से मानता है कि स्ट्रिंग को एकल-उद्धरण 'वर्ण का उपयोग करके उद्धृत किया जाएगा । देखें charset.c:

    /*
      Escape apostrophes by doubling them up
    
    // [ deletia 839-845 ]
    
      DESCRIPTION
        This escapes the contents of a string by doubling up any apostrophes that
        it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
        effect on the server.
    
    // [ deletia 852-858 ]
    */
    
    size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
                                   char *to, size_t to_length,
                                   const char *from, size_t length)
    {
    // [ deletia 865-892 ]
    
        if (*from == '\'')
        {
          if (to + 2 > to_end)
          {
            overflow= TRUE;
            break;
          }
          *to++= '\'';
          *to++= '\'';
        }

    तो, यह दोहरे-भाव वाले "पात्रों को अछूता छोड़ देता है (और सभी एकल-उद्धरण 'वर्णों को दोगुना कर देता है ) वास्तविक चरित्र के बावजूद जो शाब्दिक रूप से उद्धृत किया जाता है ! हमारे मामले $varमें यह तर्क बिल्कुल वैसा ही है जैसा कि प्रदान किया गया था, mysql_real_escape_string()जैसे कि कोई पलायन बिल्कुल नहीं हुआ है

  4. पूछताछ

    mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

    औपचारिकता के कुछ, प्रदान की गई क्वेरी है:

    SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1

जैसा कि मेरे सीखा दोस्त ने इसे रखा: बधाई, आपने एक प्रोग्राम का उपयोग करके सफलतापूर्वक हमला किया mysql_real_escape_string()...

खराब

mysql_set_charset()मदद नहीं कर सकता, क्योंकि यह चरित्र सेट के साथ कुछ नहीं करना है; और न ही mysqli::real_escape_string(), क्योंकि यह उसी फ़ंक्शन के आसपास सिर्फ एक अलग आवरण है।

समस्या, यदि पहले से ही स्पष्ट नहीं है, तो यह है कि कॉल को mysql_real_escape_string() पता नहीं चल सकता है कि किस चरित्र के साथ शाब्दिक रूप से उद्धृत किया जाएगा, क्योंकि बाद में निर्णय लेने के लिए डेवलपर पर छोड़ दिया जाता है। तो, NO_BACKSLASH_ESCAPESमोड में, शाब्दिक रूप से कोई तरीका नहीं है कि यह फ़ंक्शन सुरक्षित रूप से मनमाने ढंग से उद्धरण के साथ उपयोग के लिए हर इनपुट से बच सकता है (कम से कम, बिना दोहरीकरण वाले पात्रों के लिए जिन्हें दोहरीकरण की आवश्यकता नहीं है और इस प्रकार आपके डेटा को मग करना है)।

बदसूरत

ये और ख़राब हो जाता है। NO_BACKSLASH_ESCAPESमानक SQL के साथ संगतता के लिए इसके उपयोग की आवश्यकता के कारण जंगली un के कारण असामान्य नहीं है (जैसे SQL-92 विनिर्देश के अनुभाग 5.3 देखें , अर्थात् <quote symbol> ::= <quote><quote>व्याकरण उत्पादन और बैकस्लैश के लिए दिए गए किसी विशेष अर्थ की कमी)। इसके अलावा, इसके उपयोग को स्पष्ट रूप से (फिक्स्ड के बाद से लंबे समय तक) बग के रूप में अनुशंसित किया गया था जो कि ircmaxell के पोस्ट का वर्णन करता है। कौन जानता है, कुछ डीबीए इसे डिफ़ॉल्ट रूप से भी कॉन्फ़िगर कर सकते हैं जैसे कि गलत भागने के तरीकों के उपयोग को हतोत्साहित करना addslashes()

इसके अलावा, एक नए कनेक्शन का SQL मोड सर्वर द्वारा उसके कॉन्फ़िगरेशन के अनुसार सेट किया जाता है (जो SUPERकिसी भी समय उपयोगकर्ता बदल सकता है); इस प्रकार, सर्वर के व्यवहार के कुछ होने के लिए आपको चाहिए हमेशा स्पष्ट रूप से जोड़ने के बाद अपने वांछित मोड निर्दिष्ट करें।

द सेविंग ग्रेस

जब तक आप हमेशा स्पष्ट रूप से SQL मोड को शामिल नहीं करते हैं NO_BACKSLASH_ESCAPES, या एकल-उद्धरण वर्ण का उपयोग करके MySQL स्ट्रिंग शाब्दिकता को उद्धृत नहीं करते हैं , तब तक यह बग अपने बदसूरत सिर को पीछे नहीं कर सकता है: क्रमशः escape_quotes_for_mysql()इसका उपयोग नहीं किया जाएगा, या इसकी धारणा जिसके बारे में बोली वर्णों को दोहराने की आवश्यकता होगी। सही होना।

इस कारण से, मैं सलाह देता हूं कि कोई NO_BACKSLASH_ESCAPESभी व्यक्ति ANSI_QUOTESमोड को सक्षम करता है, क्योंकि यह एकल-उद्धृत स्ट्रिंग शाब्दिक उपयोग की आदत डालेगा। ध्यान दें कि यह एसक्यूएल इंजेक्शन को उस घटना में नहीं रोकता है जो दोहरे-उद्धृत शाब्दिक उपयोग के लिए होता है - यह केवल उस होने की संभावना को कम करता है (क्योंकि सामान्य, गैर-दुर्भावनापूर्ण प्रश्न विफल हो जाएंगे)।

पीडीओ में, इसके समतुल्य कार्य PDO::quote()और इसके तैयार किए गए स्टेटमेंट एमुलेटर दोनों पर-यही कहता है mysql_handle_quoter(): यह सुनिश्चित करता है कि बच गए शाब्दिक को सिंगल-कोट्स में उद्धृत किया गया है, इसलिए आप निश्चित हो सकते हैं कि पीडीओ हमेशा इस बग से प्रतिरक्षा रखता है।

MySQL v5.7.6 के रूप में, इस बग को ठीक कर दिया गया है। परिवर्तन लॉग देखें :

जोड़ा या बदला हुआ कार्य

  • असंगत परिवर्तन: एक नया C API फ़ंक्शन,mysql_real_escape_string_quote()को एक प्रतिस्थापन के रूप में लागू किया गया हैmysql_real_escape_string()क्योंकि बाद वाला फ़ंक्शनNO_BACKSLASH_ESCAPESSQL मोड सक्षमहोने पर वर्णों को ठीक से एन्कोड करने में विफल हो सकताहै। इस स्थिति में,mysql_real_escape_string()उद्धरण वर्णों को दोहरीकरण के अलावा नहीं छोड़ सकते, और इसे ठीक से करने के लिए, यह उपलब्ध संदर्भ के बारे में अधिक जानकारी होनी चाहिए, जो उपलब्ध है। mysql_real_escape_string_quote()उद्धरण के संदर्भ को निर्दिष्ट करने के लिए एक अतिरिक्त तर्क लेता है। उपयोग के विवरण के लिए, mysql_real_escape_string_quote () देखें।

     ध्यान दें

    सक्षम होने के mysql_real_escape_string_quote()बजाय अनुप्रयोगों का उपयोग करने के लिए संशोधित किया जाना चाहिए mysql_real_escape_string(), जो अब विफल हो जाता है और एक CR_INSECURE_API_ERRत्रुटि पैदा करता NO_BACKSLASH_ESCAPESहै।

    संदर्भ: बग # 19211994 भी देखें

सुरक्षित उदाहरण

Ircmaxell द्वारा बताए गए बग के साथ मिलकर, निम्नलिखित उदाहरण पूरी तरह से सुरक्षित हैं (यह मानते हुए कि कोई MySQL का उपयोग बाद में 4.1.20, 5.0.22, 5.1.11 से कर रहा है, या कि कोई GBK / Big5 कनेक्शन एन्कोडिंग का उपयोग नहीं कर रहा है) :

mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

... क्योंकि हमने स्पष्ट रूप से एक SQL मोड का चयन किया है जिसमें शामिल नहीं है NO_BACKSLASH_ESCAPES

mysql_set_charset($charset);
$var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

... क्योंकि हम सिंगल-कोट्स के साथ हमारे स्ट्रिंग शाब्दिक उद्धरण दे रहे हैं।

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);

... क्योंकि पीडीओ तैयार बयान इस भेद्यता से प्रतिरक्षा कर रहे हैं (और ircmaxell भी है, बशर्ते कि आप PHP.65.3.6 का उपयोग कर रहे हैं और वर्ण सेट सही ढंग से DSN में सेट किया गया है; या उस तैयार कथन अनुकरण को अक्षम कर दिया गया है) ।

$var  = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

... क्योंकि पीडीओ का quote()कार्य न केवल शाब्दिक रूप से बच जाता है, बल्कि इसे उद्धृत भी करता है (एकल-उद्धरण 'वर्णों में); ध्यान दें कि इस मामले में ircmaxell के बग से बचने के लिए, आपको PHP.35.3.6 का उपयोग करना होगा और DSN में निर्धारित वर्ण को सही ढंग से सेट करना होगा ।

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

... क्योंकि MySQLi तैयार स्टेटमेंट सुरक्षित हैं।

समेट रहा हु

इस प्रकार, यदि आप:

  • देशी तैयार कथनों का उपयोग करें

या

  • MySQL v5.7.6 या बाद का उपयोग करें

या

  • में इसके अलावा के कम से कम एक में ircmaxell के सारांश में समाधानों में से एक, उपयोग को रोजगार के लिए:

    • पीडीओ;
    • एकल-उद्धृत स्ट्रिंग शाब्दिक; या
    • एक स्पष्ट रूप से सेट SQL मोड जिसमें शामिल नहीं है NO_BACKSLASH_ESCAPES

... तो आपको पूरी तरह से सुरक्षित होना चाहिए (स्ट्रिंग के एक तरफ बाहर की तरफ कमजोरियां)।


10
तो, TL; DR जैसा होगा "एक NO_BACKSLASH_ESCAPES mysql सर्वर मोड है जो एक इंजेक्शन का कारण बन सकता है यदि आप एकल उद्धरणों का उपयोग नहीं कर रहे हैं।
आपका कॉमन सेंस

मैं bugs.mysql.com/bug.php?id=72458 तक पहुंचने में सक्षम नहीं हूं ; मुझे बस एक एक्सेस अस्वीकृत पृष्ठ मिलता है। क्या सुरक्षा मुद्दा होने के कारण इसे जनता से छिपाया जा रहा है? इसके अलावा, क्या मैं इस जवाब से सही ढंग से समझता हूं कि आप भेद्यता के खोजकर्ता हैं? यदि हां, तो बधाई।
मार्क ऐमी

1
लोगों को "पहली जगह में तार के लिए उपयोग नहीं किया जाना चाहिए । SQL कहता है कि पहचानकर्ताओं के लिए है। लेकिन एह ... MySQL का एक और उदाहरण "स्क्रैच मानकों, मैं जो कुछ भी मैं चाहता हूँ वह करूँगा"। (सौभाग्य से, आप ANSI_QUOTESउद्धरण विच्छेद को ठीक करने के लिए मोड में शामिल कर सकते हैं । मानकों की खुली अवहेलना, हालांकि, एक बड़ा मुद्दा है जिसे अधिक गंभीर उपायों की आवश्यकता हो सकती है।)
cHao

2
@ डानलेन: मेरा उत्तर थोड़ा व्यापक था, जिसमें आप पीडीओ के कार्य के माध्यम से इस विशेष बग से बच सकते हैं- लेकिन तैयार किए गए बयान आम तौर पर इंजेक्शन से बचने के लिए बहुत सुरक्षित और अधिक उपयुक्त तरीका है। बेशक, अगर आपने अपनी एसक्यूएल में सीधे अनसेप्टेड वेरिएबल्स को कंसट्रैक्ट किया है, तो आप निश्चित रूप से इंजेक्शन के लिए कमजोर हैं, भले ही आप उसके बाद किसी भी तरीके का उपयोग न करें। quote()
अर्ग्याल

1
@eggyall: हमारा सिस्टम ऊपर दिए गए 2 सुरक्षित उदाहरण पर निर्भर करता है। ऐसी त्रुटियां हैं, जहां mysql_real_escape_string को छोड़ दिया गया है। आपातकालीन मोड में उन लोगों को ठीक करना विवेकपूर्ण मार्ग लगता है, उम्मीद है कि हम सुधारों से पहले नहीं मिलेंगे। मेरा तर्क तैयार किए गए कथनों में परिवर्तित हो रहा है, एक बहुत लंबी प्रक्रिया होगी जिसके बाद आना होगा। क्या कारण तैयार बयान इस तथ्य को सुरक्षित रखते हैं कि त्रुटियां कमजोरियां पैदा नहीं करती हैं? दूसरे शब्दों में, सही ढंग से लागू किया गया दूसरा उदाहरण ऊपर दिए गए बयानों की तरह ही सुरक्षित है?
डैनैन

18

खैर, वहाँ वास्तव में कुछ भी नहीं है जो कि %वाइल्डकार्ड के अलावा अन्य माध्यम से गुजर सकता है । यह खतरनाक हो सकता है यदि आप LIKEस्टेटमेंट का उपयोग कर रहे थे क्योंकि हमलावर सिर्फ %लॉगिन कर सकता है यदि आप उसे फ़िल्टर नहीं करते हैं, और आपको अपने किसी भी उपयोगकर्ता के पासवर्ड को केवल bruteforce करना होगा। लोग अक्सर इसे 100% सुरक्षित बनाने के लिए तैयार किए गए कथनों का उपयोग करने का सुझाव देते हैं, क्योंकि डेटा उस तरह से स्वयं क्वेरी नहीं कर सकता है। लेकिन इस तरह के सरल प्रश्नों के लिए यह शायद कुछ करने के लिए अधिक कुशल होगा$login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);


2
+1, लेकिन वाइल्डकार्ड LIKE क्लॉज के लिए हैं, न कि साधारण समानता।
डोर

7
किस उपाय से आप more efficientतैयार किए गए कथनों का उपयोग करने की तुलना में एक साधारण प्रतिस्थापन पर विचार करते हैं ? (तैयार किए गए बयान हमेशा काम करते हैं, हमलों के मामले में पुस्तकालय को जल्दी से ठीक किया जा सकता है, मानव त्रुटि को उजागर नहीं करता है [जैसे कि पूर्ण प्रतिस्थापन स्ट्रिंग को गलत टाइप करना], और महत्वपूर्ण प्रदर्शन लाभ हैं यदि कथन का फिर से उपयोग किया जाता है।)
MatBailie

7
@ स्लाव: आप प्रभावी रूप से केवल शब्द वर्णों के लिए उपयोगकर्ता नाम और पासवर्ड सीमित कर रहे हैं। अधिकांश लोग जो सुरक्षा के बारे में कुछ भी जानते हैं, वे इसे एक बुरा विचार मानते हैं, क्योंकि यह खोज स्थान को काफी कम कर देता है। कोर्स वे डेटाबेस में क्लियरटेक्स्ट पासवर्ड स्टोर करने के लिए इसे एक बुरा विचार मानते हैं, लेकिन हमें समस्या को कम करने की आवश्यकता नहीं है। :)
cHao

2
@ cHao, मेरा सुझाव केवल लॉगिन की चिंता करता है। जाहिर है कि आपको पासवर्ड फ़िल्टर करने की आवश्यकता नहीं है, क्षमा करें यह स्पष्ट रूप से मेरे उत्तर में नहीं बताया गया है। लेकिन वास्तव में यह अच्छा विचार हो सकता है। हार्ड-टू-रिमेम्बर एंड-टाइप "a4üua3! @V \" ä90; 8f "के बजाय" स्टोन इग्नोरेंट ट्री स्पेस "का उपयोग करना बहुत कठिन होगा। यहां तक ​​कि शब्दकोश के 3000 शब्दों का उपयोग करके, आपकी मदद करने के लिए, जानते हुए भी 3000 शब्द कहें। आपने ठीक 4 शब्दों का उपयोग किया है - जो अभी भी लगभग 3.3 * 10 ^ 12 संयोजन होगा। :)
स्लाव

2
@ स्लाव: मैंने उस विचार को पहले देखा है; xkcd.com/936 देखें । समस्या यह है कि गणित इसे सहन नहीं करता है। आपके उदाहरण 17-चार पासवर्ड में 96 ^ 17 संभावनाएं होंगी, और यदि आप umlauts भूल गए और अपने आप को मुद्रण योग्य ASCII तक सीमित कर लिया। यह लगभग 4.5x10 ^ 33 है। हम शाब्दिक रूप से बल देने के लिए एक अरब खरब गुना अधिक काम कर रहे हैं । यहां तक ​​कि 8-चार ASCII पासवर्ड में 7.2x10 ^ 15 संभावनाएं होंगी - 3 हजार गुना अधिक।
cHao
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.