परिचय: PDO के मिथक और वास्तविकता
\n\nयदि आप PHP में कोड लिखते हैं और कभी "SQL-इंजेक्शन से कैसे बचें" गूगल किया है, तो आपने शायद यह मंत्र सुना होगा: "PDO और prepared statements का उपयोग करें - और आप सुरक्षित रहेंगे"। यह PHP सुरक्षा पर हर पाठ्यपुस्तक का स्वर्णिम मानक है। लेकिन क्या वास्तव में ऐसा है? क्या केवल $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?') लिखना और समस्या को भूल जाना पर्याप्त है?
Stack Overflow पर प्रश्न "क्या PDO prepared statements SQL इंजेक्शन को रोकने के लिए पर्याप्त हैं?" ने हजारों बार देखा गया है, यह व्यर्थ नहीं है। जैसा कि पता चलता है, उत्तर इतना सीधा नहीं है। इस लेख में, हम PDO के हुड के नीचे जाएंगे, समझेंगे कि prepared statements वास्तव में कैसे काम करते हैं, और पता लगाएंगे कि किन मामलों में PDO भी आपको निराश कर सकता है।
\n\nयह उबाऊ सिद्धांत नहीं है। हम वास्तविक उदाहरणों, क्वेरी एमुलेशन, एन्कोडिंग की बारीकियों पर विचार करेंगे और ठोस action items देंगे ताकि आपका कोड वास्तव में सुरक्षित हो।
\n\nPrepared Statements कैसे काम करते हैं: जादू या गणित?
\n\nपहले, आइए याद करें कि SQL-इंजेक्शन क्या है। यह तब होता है जब कोई हमलावर डेटा को क्वेरी में इस तरह भेजता है कि वह SQL कोड के हिस्से के रूप में व्याख्यायित हो। क्लासिक: ' OR 1=1 --।
Prepared statements इस समस्या को मौलिक रूप से हल करते हैं। वे क्वेरी के तर्क और डेटा को अलग करते हैं। यह इस तरह दिखता है:
\n\n<?php\n$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');\n$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');\n$stmt->execute([':email' => $_GET['email'], ':status' => 'active']);\n?>\n\n\nहुड के नीचे निम्नलिखित होता है:
\n\n- \n
- तैयारी (Prepare): ड्राइवर डेटाबेस सर्वर को क्वेरी का "कंकाल" भेजता है:
SELECT * FROM users WHERE email = ? AND status = ?। सर्वर इस कंकाल को पार्स करता है, इसे ऑप्टिमाइज़ करता है और निष्पादन योजना को याद रखता है। डेटा के लिए स्थान (प्लेसहोल्डर) अभी खाली हैं। \n - निष्पादन (Execute): अब आप केवल वास्तविक मान भेजते हैं। सर्वर उन्हें पहले से तैयार योजना में डालता है, लेकिन यह एक विशेष तरीके से करता है - यह जानता है कि ये डेटा हैं, कोड नहीं। इसलिए, "कोटेशन बंद करने" या SQL कमांड जोड़ने का कोई भी प्रयास केवल स्ट्रिंग लिटरल का हिस्सा बन जाता है। \n
यह आदर्श लगता है। लेकिन एक बारीकी है।
\n\nछिपा हुआ खतरा: Prepared Statements की एमुलेशन
\n\nमुख्य आश्चर्य जो Stack Overflow पर उस प्रश्न ने उजागर किया: PDO डिफ़ॉल्ट रूप से हमेशा "वास्तविक" prepared statements का उपयोग नहीं करता।
\n\nबात यह है कि सभी डेटाबेस प्रोटोकॉल स्तर पर इस सुविधा का समर्थन नहीं करते हैं। और कुछ ड्राइवर (जैसे SQLite या MySQL के पुराने संस्करणों के लिए) एमुलेशन के माध्यम से काम करते हैं। इसका क्या मतलब है?
\n\nजब एमुलेशन चालू होता है, PDO स्वयं PHP में क्लाइंट साइड पर SQL क्वेरी में मान डालता है, और फिर तैयार क्वेरी को सर्वर को भेजता है। ऐसा करते समय, यह PDO::quote() का उपयोग करके विशेष वर्णों को एस्केप करता है।
और यहीं से समस्याएँ शुरू होती हैं। एमुलेशन, संक्षेप में, वही स्ट्रिंग प्रतिस्थापन है, जो केवल लाइब्रेरी द्वारा किया जाता है। और यदि आपने कनेक्शन एन्कोडिंग गलत सेट की है, तो यह तंत्र हैक किया जा सकता है।
\n\nउदाहरण: विभिन्न एन्कोडिंग के माध्यम से हमला
\n\nगलत एन्कोडिंग का उपयोग करके एक क्लासिक हमला इस तरह दिखता है। मान लीजिए, आपने MySQL के साथ GBK (चीनी एन्कोडिंग) में कनेक्शन स्थापित किया है, और क्वेरी एमुलेट की जा रही है। GBK में \\ (बैकस्लैश) वर्ण एक मल्टी-बाइट वर्ण का हिस्सा हो सकता है।
हमलावर स्ट्रिंग भेजता है: %BF%27। GBK एन्कोडिंग में, अनुक्रम %BF%5C एक वर्ण बनाता है, और %27 बस एक कोटेशन है। एमुलेटर कोटेशन देखता है और इसे एस्केप करता है, %27 को %5C%27 (अर्थात \\') में बदल देता है। लेकिन इस वजह से कि