परिचय: वह प्रश्न जो हर PHP डेवलपर को परेशान करता है
\n\nयदि आपने कभी PHP में SQL क्वेरी लिखी है, तो आपने निश्चित रूप से यह मंत्र सुना होगा: "PDO और prepared statements का उपयोग करें - और आप सुरक्षित रहेंगे"। लेकिन क्या वास्तव में ऐसा है? Stack Overflow पर प्रश्न "Are PDO prepared statements sufficient to prevent SQL injection?" ने हजारों बार देखा गया और दर्जनों गरमागरम चर्चाएँ हुईं। और यह व्यर्थ नहीं है।
\n\nइस लेख में हम केवल "हाँ" या "नहीं" का संक्षिप्त उत्तर नहीं देंगे। हम PDO के अंदरूनी तंत्र में गहराई से उतरेंगे, यह समझेंगे कि तैयार क्वेरीज़ वास्तव में कैसे काम करती हैं, और पता लगाएंगे कि किन मामलों में PDO भी आपको निराश कर सकता है। तैयार हैं? चलिए शुरू करते हैं।
\n\nSQL इंजेक्शन क्या है और यह खतरनाक क्यों है?
\n\nसुरक्षा के बारे में बात करने से पहले, आइए दुश्मन को पहचानें। SQL इंजेक्शन एक हमला है जिसमें हमलावर इनपुट डेटा के माध्यम से दुर्भावनापूर्ण SQL कोड "इंजेक्ट" करता है। क्लासिक उदाहरण:
\n\n// खराब कोड: ऐसा कभी न करें\n$username = $_POST['username'];\n$query = "SELECT * FROM users WHERE username = '$username'";\n$result = mysqli_query($conn, $query);\n\n\nयदि हमलावर admin' OR '1'='1 भेजता है, तो क्वेरी बन जाती है:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'\n\n\nवोइला - पासवर्ड के बिना डेटा तक पहुंच। और यदि वह '; DROP TABLE users; -- भेजता है? अलविदा, उपयोगकर्ता तालिका।
PDO और तैयार क्वेरीज़: यह कैसे काम करता है?
\n\nPDO (PHP Data Objects) prepared statements (तैयार अभिव्यक्तियाँ) का तंत्र प्रदान करता है। विचार सरल है: आप क्वेरी की संरचना को डेटा से अलग करते हैं। वेरिएबल्स को सीधे स्ट्रिंग में डालने के बजाय, आप प्लेसहोल्डर का उपयोग करते हैं:
\n\n// PDO के साथ सुरक्षित कोड\n$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");\n$stmt->execute([':username' => $_POST['username']]);\n\n\nलेकिन अंदरूनी तंत्र में क्या होता है? यह महत्वपूर्ण बिंदु है जिसे कई लोग अनदेखा करते हैं।
\n\nएमुलेशन मोड बनाम नेटिव prepared statements
\n\nPDO दो मोड में काम कर सकता है:
\n\n- \n
- एमुलेशन मोड (पुराने PHP संस्करणों में डिफ़ॉल्ट) - PDO स्वयं डेटा को एस्केप करता है और उन्हें PHP की ओर से क्वेरी में डालता है। यह अभी भी सुरक्षित है, लेकिन सैद्धांतिक रूप से कमजोर है यदि एस्केपिंग विफल हो जाती है (उदाहरण के लिए, गैर-मानक एन्कोडिंग के साथ काम करते समय)। \n
- नेटिव मोड - PDO डेटाबेस सर्वर को दो संदेश भेजता है: पहले क्वेरी का टेम्पलेट, फिर अलग से डेटा। सर्वर स्वयं डेटा को मान के रूप में संसाधित करता है, कोड के रूप में नहीं। यह पूरी तरह से सुरक्षित है। \n
नेटिव मोड सक्षम करने के लिए, एमुलेशन बंद करें:
\n\n$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);\n\n\nअब क्वेरी दो चरणों में सर्वर को भेजी जाती है। भले ही हमलावर ' OR '1'='1 भेजे, MySQL या PostgreSQL सर्वर इसे केवल एक स्ट्रिंग मान के रूप में समझेगा, SQL कोड के भाग के रूप में नहीं। जादू!
लेकिन क्या सब कुछ इतना आदर्श है? PDO के नुकसान
\n\nStack Overflow के प्रश्न पर वापस आते हैं। उत्तर "हाँ, PDO prepared statements पर्याप्त हैं" - लेकिन कुछ शर्तों के साथ। यहाँ वे स्थितियाँ हैं जब सुरक्षा विफल हो सकती है।
\n\n1. डायनामिक आइडेंटिफ़ायर (तालिकाओं, स्तंभों के नाम)
\n\nPrepared statements केवल मानों (डेटा) के लिए काम करते हैं। यदि आप तालिका या स्तंभ का नाम डालते हैं, तो प्लेसहोल्डर मदद नहीं करेंगे:
\n\n// खतरनाक! तालिका का नाम प्लेसहोल्डर के माध्यम से नहीं भेजा जा सकता\n$stmt = $pdo->prepare("SELECT * FROM :table WHERE id = :id");\n$stmt->execute([':table' => $_GET['table'], ':id' => 1]); // त्रुटि!\n\n\nPDO बस तालिका के नाम को एक स्ट्रिंग के रूप में डालेगा, और क्वेरी टूट जाएगी। यदि आप एमुलेशन का उपयोग करते हैं, तो हमलावर users; DROP TABLE users; -- भेज सकता है। समाधान: अनुमत मानों की श्वेत सूची।
2. LIKE क्वेरीज़ और वाइल्डकार्ड वर्ण
\n\nयदि आप उपयोग कर रहे हैं