php एसक्यूएल इंजेक्शन जो mysql_real_escape_string() के आसपास आता है




security sql-injection (4)

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

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

इस नमूना की स्थिति पर विचार करें। एसक्यूएल इस तरह 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 कोड के माध्यम से प्राप्त होगा?


टी एल; डॉ

mysql_real_escape_string() कोई भी सुरक्षा प्रदान नहीं करेगा (और आपके डेटा को आगे बढ़ा सकता है) यदि:

  • MySQL का NO_BACKSLASH_ESCAPES SQL मोड सक्षम है (जो हो सकता है, जब तक आप कनेक्ट करते समय हर बार SQL सर्वर को स्पष्ट रूप से चुनते हैं ); तथा

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

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

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

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

आक्रमण

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

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. एक एसक्यूएल मोड का चयन करना

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

    स्ट्रिंग लिटरेल्स के तहत दस्तावेज के रूप में:

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

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

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

    • एक बचने वाले चरित्र (" \ ") द्वारा उद्धरण चरित्र से पहले।

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

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

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

बचत अनुग्रह

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

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

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

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

कार्यक्षमता जोड़ा या बदला गया

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

    ध्यान दें

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

    संदर्भ: बग # 1 9 211 99 4 भी देखें।

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

Ircmaxell द्वारा समझाए गए बग के साथ एक साथ लिया गया, निम्नलिखित उदाहरण पूरी तरह से सुरक्षित हैं (मानते हैं कि कोई या तो 4.1.20, 5.0.22, 5.1.11 से बाद में MySQL का उपयोग कर रहा है या वह कोई 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≥5.3.6 का उपयोग कर रहे हों और चरित्र सेट को डीएसएन में सही ढंग से सेट किया गया हो या वह तैयार कथन अनुकरण अक्षम कर दिया गया हो) ।

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

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

$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 शामिल नहीं है

... तो आपको पूरी तरह से सुरक्षित होना चाहिए (स्ट्रिंग के दायरे के बाहर भेद्यताएं अलग-अलग हो रही हैं)।


संक्षिप्त उत्तर हाँ है, हाँ 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');
    

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

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

  2. पेलोड

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

    हमने इस पेलोड को चुना है क्योंकि, अगर हम उस पर addslashes() कहते हैं, तो हम ' अक्षर ' से पहले ASCII \ या 0x5c । तो हम 0xbf5c27 साथ हवा में 0xbf5c27 , जो gbk में दो वर्ण अनुक्रम है: 0xbf5c बाद। या दूसरे शब्दों में, एक अज्ञात चरित्र के बाद एक अनजान ' । लेकिन हम addslashes() का उपयोग नहीं कर रहे हैं। तो अगले कदम पर ...

  3. mysql_real_escape_string ()

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

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

    縗' OR 1=1 /*

    हमले के लिए वास्तव में क्या आवश्यक है।

  4. पूछताछ

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

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

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

खराब

ये और ख़राब हो जाता है। PDO MySQL के साथ तैयार बयानों को अनुकरण करने के लिए डिफ़ॉल्ट है। इसका मतलब है कि ग्राहक पक्ष पर, यह मूल रूप से mysql_real_escape_string() (सी लाइब्रेरी में mysql_real_escape_string() माध्यम से एक sprintf करता है, जिसका अर्थ है कि निम्नलिखित सफल इंजेक्शन होगा:

$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);

इसका आमतौर पर एक सच्चे तैयार कथन (यानी क्वेरी से अलग पैकेट में डेटा भेजा जा रहा है) का परिणाम होगा। हालांकि, इस बात से अवगत रहें कि पीडीओ चुपचाप बयानों को अनुकरण करने के लिए fallback करेगा कि MySQL मूल रूप से तैयार नहीं हो सकता है: जो मैन्युअल में listed हो सकते हैं, लेकिन उचित सर्वर संस्करण का चयन करने के लिए सावधान रहें)।

बदसूरत

मैंने बहुत शुरुआत में कहा कि अगर हम SET NAMES gbk बजाय mysql_set_charset('gbk') का इस्तेमाल करते हैं तो हम इन सब को रोक सकते थे। और यह सच है बशर्ते आप 2006 से एक MySQL रिलीज का उपयोग कर रहे हों।

यदि आप पहले MySQL रिलीज़ का उपयोग कर रहे हैं, तो mysql_real_escape_string() में एक bug का अर्थ है कि हमारे पेलोड में मौजूद अमान्य मल्टीबाइट वर्णों को एस्केपिंग उद्देश्यों के लिए एकल बाइट्स के रूप में माना जाता था, भले ही क्लाइंट को कनेक्शन एन्कोडिंग के बारे में सही तरीके से सूचित किया गया हो और यह हमला अभी भी सफल होगा। बग MySQL 4.1.20 , 5.0.22 और 5.1.11 में तय किया गया था।

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

बचत अनुग्रह

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

वैकल्पिक रूप से, आप NO_BACKSLASH_ESCAPES SQL मोड सक्षम कर सकते हैं, जो (अन्य चीजों के साथ) mysql_real_escape_string() के संचालन को बदल देता है। इस मोड को सक्षम करने के साथ, 0x27 को 0x2727 बजाय 0x2727 साथ प्रतिस्थापित किया जाएगा और इस प्रकार से बचने की प्रक्रिया किसी भी कमजोर एन्कोडिंग में मान्य वर्ण नहीं बना सकती है, जहां वे पहले मौजूद नहीं थे (यानी 0xbf27 अभी भी 0xbf27 आदि है) - तो सर्वर अभी भी स्ट्रिंग को अमान्य के रूप में अस्वीकार करें। हालांकि, इस SQL ​​मोड का उपयोग करने से उत्पन्न होने वाली भिन्न भेद्यता के लिए @ eggyal का उत्तर देखें।

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

निम्नलिखित उदाहरण सुरक्षित हैं:

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() / पीडीओ का $mysqli->set_charset() वर्णसेट पैरामीटर (PHP ≥ 5.3.6 में)

या

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

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

अन्यथा, आप कमजोर हैं भले ही आप mysql_real_escape_string() का उपयोग कर रहे हों ...


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

$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";




sql-injection