php - पीडीओ एसक्यूएल इंजेक्शन को रोकने के लिए पर्याप्त बयान तैयार हैं?




security pdo (5)

तैयार कथन / पैरामीटरयुक्त प्रश्न आमतौर पर उस कथन * पर 1 ऑर्डर इंजेक्शन को रोकने के लिए पर्याप्त होते हैं। यदि आप अपने आवेदन में कहीं भी अन-चेक किए गए गतिशील एसक्यूएल का उपयोग करते हैं तो आप अभी भी दूसरे ऑर्डर इंजेक्शन के लिए कमजोर हैं।

द्वितीय ऑर्डर इंजेक्शन का मतलब है कि किसी क्वेरी में शामिल होने से पहले डेटा को डाटाबेस के माध्यम से साइकिल चलाया गया है, और इसे खींचना बहुत मुश्किल है। AFAIK, आप लगभग कभी भी इंजीनियर द्वारा दूसरे ऑर्डर हमलों को कभी नहीं देखते हैं, क्योंकि आमतौर पर हमलावरों के लिए सामाजिक-इंजीनियर के लिए आसान होता है, लेकिन कभी-कभी आपके पास अतिरिक्त सौम्य वर्णों या समान के कारण दूसरी ऑर्डर बग फसल होती है।

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

' + (SELECT UserName + '_' + Password FROM Users LIMIT 1) + '

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

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

* यह पता चला है कि MySQL / PHP हैं (ठीक है, थे) विस्तृत अक्षर शामिल होने पर पैरामीटर को संभालने के बारे में सिर्फ गूंगा है, और यहां पर एक अत्यधिक दुर्लभ मामला है जो यहां अत्यधिक उच्च वोट वाले उत्तर में उल्लिखित है जो इंजेक्शन को पैरामीटरकृत के माध्यम से पर्ची करने की अनुमति दे सकता है क्वेरी।

मान लें कि मेरे पास कोड है:

$dbh = new PDO("blahblah");

$stmt = $dbh->prepare('SELECT * FROM users where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

पीडीओ दस्तावेज कहता है:

तैयार बयानों के मानकों को उद्धृत करने की आवश्यकता नहीं है; चालक इसे आपके लिए संभालता है।

एसक्यूएल इंजेक्शन से बचने के लिए मुझे वास्तव में क्या करना है? क्या वाकई वह इतना आसान है?

यदि आप कोई फर्क पड़ता है तो आप MySQL मान सकते हैं। इसके अलावा, मैं वास्तव में केवल एसक्यूएल इंजेक्शन के खिलाफ तैयार बयान के उपयोग के बारे में उत्सुक हूं। इस संदर्भ में, मुझे एक्सएसएस या अन्य संभावित भेद्यता की परवाह नहीं है।


नहीं यह पर्याप्त नहीं है (कुछ विशिष्ट मामलों में)! डिफ़ॉल्ट रूप से पीडीओ डेटाबेस ड्राइवर के रूप में MySQL का उपयोग करते समय नकली तैयार बयान का उपयोग करता है। MySQL और PDO का उपयोग करते समय आपको हमेशा नकली तैयार कथन अक्षम करना चाहिए:

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

एक और चीज जो हमेशा किया जाना चाहिए, यह डेटाबेस के सही एन्कोडिंग को सेट करता है:

$dbh = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

यह संबंधित प्रश्न भी देखें: मैं PHP में एसक्यूएल इंजेक्शन को कैसे रोक सकता हूं?

यह भी ध्यान रखें कि केवल उन चीजों के डेटाबेस पक्ष के बारे में है जिन्हें डेटा प्रदर्शित करते समय आपको स्वयं को देखना होगा। जैसे कि सही एन्कोडिंग और उद्धरण शैली के साथ htmlspecialchars() का उपयोग करके।


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


संक्षिप्त उत्तर नहीं है , पीडीओ तैयार करता है आप सभी संभावित एसक्यूएल-इंजेक्शन हमलों से आपकी रक्षा नहीं करेगा। कुछ अस्पष्ट किनारे के मामलों के लिए।

मैं पीडीओ के बारे में बात करने के लिए इस जवाब का अनुकूलन कर रहा हूं ...

लंबा जवाब इतना आसान नहीं है। यह यहां प्रदर्शित एक हमले पर आधारित है।

आक्रमण

तो, आक्रमण दिखाकर शुरू करें ...

$pdo->query('SET NAMES gbk');
$var = "\xbf\x27 OR 1=1 /*";
$query = 'SELECT * FROM test WHERE name = ? LIMIT 1';
$stmt = $pdo->prepare($query);
$stmt->execute(array($var));

कुछ परिस्थितियों में, यह 1 से अधिक पंक्ति वापस कर देगा। चलो विच्छेदन करें कि यहां क्या हो रहा है:

  1. एक चरित्र सेट का चयन करना

    $pdo->query('SET NAMES gbk');
    

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

    अब, SET NAMES के उपयोग को ध्यान में रखना बहुत महत्वपूर्ण है। यह सर्वर पर सेट सेट सेट करता है। ऐसा करने का एक और तरीका है, लेकिन हम जल्द ही वहां पहुंच जाएंगे।

  2. पेलोड

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

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

  3. $ stmt-> निष्पादित ()

    यहां महसूस करने की महत्वपूर्ण बात यह है कि डिफ़ॉल्ट रूप से पीडीओ सच तैयार कथन नहीं करता है। यह उन्हें emulates (MySQL के लिए)। इसलिए, पीडीओ आंतरिक रूप से क्वेरी स्ट्रिंग बनाता है, प्रत्येक बाउंड स्ट्रिंग मान पर mysql_real_escape_string() (MySQL C API फ़ंक्शन) को कॉल करता है।

    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
    

बधाई हो, आपने पीडीओ तैयार वक्तव्यों का उपयोग करके सफलतापूर्वक एक कार्यक्रम पर हमला किया ...

सरल फिक्स

अब, यह ध्यान देने योग्य है कि आप नकली तैयार बयानों को अक्षम करके इसे रोक सकते हैं:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

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

सही फिक्स

यहां समस्या यह है कि हमने SET NAMES एपीआई के बजाय सी एपीआई के mysql_set_charset() कॉल नहीं किया था। अगर हमने किया, तो हम ठीक होंगे, बशर्ते हम 2006 से एक माईएसक्यूएल रिलीज का उपयोग कर रहे हों।

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

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

बचत अनुग्रह

जैसा कि हमने शुरुआत में कहा था, इस हमले के लिए डेटाबेस कनेक्शन को काम करने के लिए एक कमजोर चरित्र सेट का उपयोग करके एन्कोड किया जाना चाहिए। 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, आदि) और पीडीओ के डीएसएन वर्णमाला पैरामीटर (PHP ≥ 5.3.6 में)

या

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

या

  • NO_BACKSLASH_ESCAPES SQL मोड सक्षम करें

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

अन्यथा, आप पीडीओ तैयार वक्तव्य का उपयोग कर रहे हैं भले ही आप कमजोर हैं ...

परिशिष्ट

मैं PHP के भविष्य के संस्करण के लिए तैयार करने के अनुकरण के लिए डिफॉल्ट को बदलने के लिए धीरे-धीरे पैच पर काम कर रहा हूं। जिस समस्या में मैं दौड़ रहा हूं वह यह है कि जब मैं ऐसा करता हूं तो बहुत सारे परीक्षण तोड़ते हैं। एक समस्या यह है कि नकली तैयार केवल निष्पादन पर सिंटैक्स त्रुटियों को फेंक देगा, लेकिन सच्चे तैयार तैयार करने पर त्रुटियों को फेंक देंगे। इसलिए इससे समस्याएं पैदा हो सकती हैं (और कारण परीक्षण का हिस्सा बर्किंग है)।


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

पैरामीटर प्रश्न कोड और डेटा को अलग से भेजकर काम करते हैं, इसलिए इसमें कोई छेद नहीं खोजना संभव नहीं होगा।

हालांकि आप अभी भी अन्य इंजेक्शन-प्रकार के हमलों के प्रति संवेदनशील हो सकते हैं। उदाहरण के लिए, यदि आप एचटीएमएल पेज में डेटा का उपयोग करते हैं, तो आप एक्सएसएस प्रकार के हमलों के अधीन हो सकते हैं।







sql-injection