टी एल; डॉ
mysql_real_escape_string()
यदि कोई सुरक्षा प्रदान नहीं करता है (और इसके अलावा आपके डेटा को कम कर सकता है):
MySQL का NO_BACKSLASH_ESCAPES
SQL मोड सक्षम है (जो कि ऐसा हो सकता है, जब तक कि आप स्पष्ट रूप से कनेक्ट होने पर हर बार किसी अन्य 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 के साथ संगतता के लिए इसके उपयोग की आवश्यकता के कारण जंगली 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 के रूप में, इस बग को ठीक कर दिया गया है। परिवर्तन लॉग देखें :
जोड़ा या बदला हुआ कार्य
सुरक्षित उदाहरण
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 या बाद का उपयोग करें
या
... तो आपको पूरी तरह से सुरक्षित होना चाहिए (स्ट्रिंग के एक तरफ बाहर की तरफ कमजोरियां)।