सही SQL इंजेक्शन हमलों से बचने का तरीका, चाहे आप किसी भी डेटाबेस का उपयोग करें, डेटा को SQL से अलग करना है , ताकि डेटा डेटा बना रहे और कभी भी व्याख्या नहीं की जाएगी SQL पार्सर द्वारा कमांड के रूप में। सही ढंग से स्वरूपित डेटा भागों के साथ SQL कथन बनाना संभव है, लेकिन यदि आप पूरी तरह . नहीं करते हैं विवरण को समझने के लिए, आपको हमेशा तैयार कथनों और पैरामीटरयुक्त प्रश्नों का उपयोग करना चाहिए। ये SQL स्टेटमेंट हैं जो डेटाबेस सर्वर द्वारा किसी भी पैरामीटर से अलग से भेजे और पार्स किए जाते हैं। इस तरह एक हमलावर के लिए दुर्भावनापूर्ण SQL को इंजेक्ट करना असंभव है।
इसे हासिल करने के लिए आपके पास मूल रूप से दो विकल्प हैं:
-
पीडीओ का उपयोग करना (किसी भी समर्थित डेटाबेस ड्राइवर के लिए):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name'); $stmt->execute([ 'name' => $name ]); foreach ($stmt as $row) { // Do something with $row }
-
MySQLi का उपयोग करना (MySQL के लिए):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?'); $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string' $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // Do something with $row }
यदि आप MySQL के अलावा किसी अन्य डेटाबेस से कनेक्ट कर रहे हैं, तो एक ड्राइवर-विशिष्ट दूसरा विकल्प है जिसे आप संदर्भित कर सकते हैं (उदाहरण के लिए, pg_prepare()
और pg_execute()
पोस्टग्रेएसक्यूएल के लिए)। पीडीओ सार्वभौमिक विकल्प है।
कनेक्शन को सही तरीके से सेट करना
ध्यान दें कि पीडीओ . का उपयोग करते समय एक MySQL डेटाबेस तक पहुँचने के लिए असली तैयार कथन डिफ़ॉल्ट रूप से उपयोग नहीं किए जाते हैं . इसे ठीक करने के लिए आपको तैयार बयानों के अनुकरण को अक्षम करना होगा। PDO . का उपयोग करके कनेक्शन बनाने का एक उदाहरण है:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
उपरोक्त उदाहरण में त्रुटि मोड कड़ाई से आवश्यक नहीं है, लेकिन इसे जोड़ने की सलाह दी जाती है . इस तरह स्क्रिप्ट एक Fatal Error
के साथ नहीं रुकेगी जब कुछ गलत हो जाता है। और यह डेवलपर को catch
. का मौका देता है कोई त्रुटि (त्रुटि) जो throw
. हैं n PDOException
. के रूप में एस.
क्या है अनिवार्य , हालांकि, पहला setAttribute()
. है लाइन, जो पीडीओ को नकली तैयार बयानों को अक्षम करने और असली . का उपयोग करने के लिए कहती है तैयार बयान। यह सुनिश्चित करता है कि MySQL सर्वर पर भेजने से पहले PHP द्वारा कथन और मानों को पार्स नहीं किया गया है (संभावित हमलावर को दुर्भावनापूर्ण SQL इंजेक्ट करने का कोई मौका नहीं)।
हालांकि आप charset
. सेट कर सकते हैं कंस्ट्रक्टर के विकल्पों में, यह ध्यान रखना महत्वपूर्ण है कि PHP के 'पुराने' संस्करण (5.3.6 से पहले) चारसेट पैरामीटर को चुपचाप अनदेखा कर दिया
डीएसएन में।
स्पष्टीकरण
आप जिस SQL कथन को prepare
करने के लिए पास करते हैं डेटाबेस सर्वर द्वारा पार्स और संकलित किया जाता है। पैरामीटर निर्दिष्ट करके (या तो ?
या एक नामित पैरामीटर जैसे :name
ऊपर के उदाहरण में) आप उस डेटाबेस इंजन को बताते हैं जहां आप फ़िल्टर करना चाहते हैं। फिर जब आप execute
. को कॉल करते हैं , तैयार कथन आपके द्वारा निर्दिष्ट पैरामीटर मानों के साथ संयुक्त है।
यहां महत्वपूर्ण बात यह है कि पैरामीटर मान संकलित कथन के साथ संयुक्त होते हैं, SQL स्ट्रिंग नहीं। SQL इंजेक्शन डेटाबेस को भेजने के लिए SQL बनाते समय दुर्भावनापूर्ण स्ट्रिंग्स को शामिल करके स्क्रिप्ट को धोखा देकर काम करता है। तो वास्तविक एसक्यूएल को पैरामीटर से अलग भेजकर, आप किसी ऐसी चीज के साथ समाप्त होने के जोखिम को सीमित करते हैं जिसका आप इरादा नहीं रखते थे।
तैयार कथन का उपयोग करते समय आपके द्वारा भेजे गए किसी भी पैरामीटर को केवल स्ट्रिंग्स के रूप में माना जाएगा (हालांकि डेटाबेस इंजन कुछ अनुकूलन कर सकता है, इसलिए पैरामीटर संख्या के रूप में भी समाप्त हो सकते हैं)। ऊपर के उदाहरण में, यदि $name
वेरिएबल में 'Sarah'; DELETE FROM employees
परिणाम केवल स्ट्रिंग की खोज होगी "'Sarah'; DELETE FROM employees"
, और आपके पास एक खाली तालिका
नहीं होगी ।
तैयार बयानों का उपयोग करने का एक और लाभ यह है कि यदि आप एक ही सत्र में एक ही बयान को कई बार निष्पादित करते हैं तो इसे केवल एक बार पार्स और संकलित किया जाएगा, जिससे आपको कुछ गति लाभ मिलेगा।
ओह, और चूंकि आपने इसे डालने के लिए कैसे करना है, इसके बारे में पूछा है, यहां एक उदाहरण है (पीडीओ का उपयोग करके):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
क्या तैयार किए गए कथनों का उपयोग गतिशील प्रश्नों के लिए किया जा सकता है?
जबकि आप अभी भी क्वेरी पैरामीटर के लिए तैयार स्टेटमेंट का उपयोग कर सकते हैं, डायनेमिक क्वेरी की संरचना को स्वयं पैरामीट्रिज नहीं किया जा सकता है और कुछ क्वेरी सुविधाओं को पैरामीट्रिज नहीं किया जा सकता है।
इन विशिष्ट परिदृश्यों के लिए, सबसे अच्छी बात एक श्वेतसूची फ़िल्टर का उपयोग करना है जो संभावित मानों को प्रतिबंधित करता है।
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}