SQL इंजेक्शन जो mysql_real_escape_string () के आसपास मिलता है
क्या 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 कोड के माध्यम से मिलने वाले किसी भी संभावित इंजेक्शन के बारे में जानते हैं?
जवाब
निम्नलिखित प्रश्न पर विचार करें:
$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";
संक्षिप्त उत्तर हाँ है, हाँ चारों ओर पाने का एक तरीका है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
। यह पता चला है के रूप में, 5 ऐसे डिफ़ॉल्ट रूप से MySQL 5.6 में समर्थित एनकोडिंग हैं: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
, तो हम देखेंगे:= 'या 1 = 1 / *
जो वास्तव में हमले की आवश्यकता है।
पूछताछ
यह हिस्सा सिर्फ एक औपचारिकता है, लेकिन यहां प्रस्तुत किया गया प्रश्न है:
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 in 5.3.6 में) का उपयोग करें
या
- कनेक्शन एन्कोडिंग के लिए एक कमजोर वर्ण सेट का उपयोग न करें (आप केवल
utf8
/latin1
/ascii
/ etc का उपयोग करें )
आप 100% सुरक्षित हैं।
अन्यथा, आप उपयोग करने के बावजूदmysql_real_escape_string()
कमजोर हैं ...
टीएल, डॉ
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
तालिका से सभी रिकॉर्ड लौटाएगा । एक विच्छेदन:
SQL मोड का चयन करना
mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');
स्ट्रिंग साहित्य के तहत प्रलेखित :
एक स्ट्रिंग के भीतर उद्धरण वर्णों को शामिल करने के कई तरीके हैं:
"
'
" के साथ उद्धृत स्ट्रिंग के अंदर एक "'
" के रूप में लिखा जा सकता है''
।"
"
" के साथ उद्धृत स्ट्रिंग के अंदर एक ""
" के रूप में लिखा जा सकता है""
।बच चरित्र ("
\
") द्वारा बोली चरित्र को प्राथमिकता दें ।"
'
" के साथ उद्धृत स्ट्रिंग के अंदर ""
" को किसी विशेष उपचार की आवश्यकता नहीं है और इसे दोगुना या बच जाने की आवश्यकता नहीं है। उसी तरह, ""
" के साथ उद्धृत एक स्ट्रिंग के अंदर "'
" कोई विशेष उपचार की आवश्यकता है।
यदि सर्वर के एसक्यूएल मोड में शामिल है NO_BACKSLASH_ESCAPES, तो इनमें से तीसरा विकल्प - जो सामान्य दृष्टिकोण है जिसे अपनाया गया है
mysql_real_escape_string()
- जो उपलब्ध नहीं है: इसके बजाय पहले दो विकल्पों में से एक का उपयोग किया जाना चाहिए। ध्यान दें कि चौथी गोली का प्रभाव यह है कि किसी व्यक्ति को आवश्यक रूप से उस चरित्र को जानना चाहिए, जिसका उपयोग शाब्दिक रूप से किसी के डेटा से बचने के लिए किया जाएगा।पेलोड
" OR 1=1 --
पेलोड इस इंजेक्शन को
"
अक्षर के साथ सचमुच में शुरू करता है। कोई विशेष एन्कोडिंग नहीं। कोई विशेष पात्र नहीं। कोई अजीब बाइट्स नहीं।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_ESCAPES
SQL मोड उपयोग में है, तो इस प्रकार एक अलग अंतर्निहित फ़ंक्शन को लागू किया जाता है। जैसा कि ऊपर उल्लेख किया गया है, इस तरह के एक फ़ंक्शन को यह जानने की जरूरत है कि शाब्दिक रूप से दोहराए जाने के लिए कौन से वर्ण का उपयोग किया जाएगा ताकि इसे दोहराए जाने से अन्य उद्धरण चरित्र पैदा न हो।हालांकि, यह फ़ंक्शन मनमाने ढंग से मानता है कि स्ट्रिंग को एकल-उद्धरण
'
वर्ण का उपयोग करके उद्धृत किया जाएगा । देखें 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()
जैसे कि कोई पलायन बिल्कुल नहीं हुआ है ।पूछताछ
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 के साथ संगतता के लिए इसके उपयोग की आवश्यकता के कारण जंगली be के कारण असामान्य नहीं है (उदाहरण के लिए 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();
...because MySQLi prepared statements are safe.
Wrapping Up
Thus, if you:
- use native prepared statements
OR
- use MySQL v5.7.6 or later
OR
in addition to employing one of the solutions in ircmaxell's summary, use at least one of:
- PDO;
- single-quoted string literals; or
- an explicitly set SQL mode that does not include
NO_BACKSLASH_ESCAPES
...then you should be completely safe (vulnerabilities outside the scope of string escaping aside).
Well, there's nothing really that can pass through that, other than %
wildcard. It could be dangerous if you were using LIKE
statement as attacker could put just %
as login if you don't filter that out, and would have to just bruteforce a password of any of your users. People often suggest using prepared statements to make it 100% safe, as data can't interfere with the query itself that way. But for such simple queries it probably would be more efficient to do something like $login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);