मैं PHP में SQL इंजेक्शन को कैसे रोक सकता हूं?


2773

यदि उपयोगकर्ता इनपुट को SQL क्वेरी में संशोधन के बिना डाला जाता है, तो आवेदन निम्न उदाहरण की तरह, SQL इंजेक्शन के लिए असुरक्षित हो जाता है :

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

ऐसा इसलिए है क्योंकि उपयोगकर्ता कुछ इनपुट कर सकता है value'); DROP TABLE table;--, और क्वेरी बन जाती है:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

ऐसा होने से रोकने के लिए क्या किया जा सकता है?

जवाबों:


8954

तैयार किए गए कथनों और मानकीकृत प्रश्नों का उपयोग करें। ये SQL कथन हैं जो डेटाबेस सर्वर द्वारा किसी भी पैरामीटर से अलग से भेजे और पार्स किए गए हैं। इस तरह एक हमलावर के लिए दुर्भावनापूर्ण SQL इंजेक्षन करना असंभव है।

इसे प्राप्त करने के लिए आपके पास मूल रूप से दो विकल्प हैं:

  1. पीडीओ का उपयोग करना (किसी भी समर्थित डेटाबेस ड्राइवर के लिए):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute([ 'name' => $name ]);
    
    foreach ($stmt as $row) {
        // Do something with $row
    }
  2. 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()PostgreSQL के लिए) संदर्भित कर सकते हैं । पीडीओ सार्वभौमिक विकल्प है।


सही ढंग से कनेक्शन स्थापित करना

ध्यान दें कि जब PDOMySQL डेटाबेस तक पहुँचने के लिए उपयोग किया जाता है तो वास्तविक रूप से तैयार किए गए विवरण डिफ़ॉल्ट रूप से उपयोग नहीं किए जाते हैं । इसे ठीक करने के लिए आपको तैयार किए गए कथनों के अनुकरण को निष्क्रिय करना होगा। पीडीओ का उपयोग करके कनेक्शन बनाने का एक उदाहरण है:

$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किसी भी त्रुटि (ओं) को मौका देता है जो thrown के रूप में हैं PDOException

हालाँकि, यह अनिवार्य है , पहली setAttribute()पंक्ति है, जो पीडीओ को तैयार किए गए कथनों को निष्क्रिय करने और वास्तविक तैयार कथनों का उपयोग करने के लिए कहती है । यह सुनिश्चित करता है कि MySQL सर्वर पर भेजने से पहले PHP द्वारा स्टेटमेंट और वैल्यूज़ को पार्स नहीं किया गया है (दुर्भावनापूर्ण एसक्यूएल को इंजेक्ट करने का कोई संभावित मौका नहीं देता)।

यद्यपि आप charsetकंस्ट्रक्टर के विकल्पों में सेट कर सकते हैं , यह ध्यान रखना महत्वपूर्ण है कि PHP के 'पुराने' संस्करणों (5.3.6 से पहले) ने चुपचाप डीएसएन में चार्ट पैरामीटर की अनदेखी की


व्याख्या

SQL कथन जिसे आप पास करते हैं prepare, उसे डेटाबेस सर्वर द्वारा पार्स और संकलित किया जाता है। मापदंडों को निर्दिष्ट करके (या तो उदाहरण में ?एक नामित पैरामीटर जैसा :nameऊपर) आप डेटाबेस इंजन को बताते हैं जहां आप फ़िल्टर करना चाहते हैं। फिर जब आप कॉल करते हैं execute, तो तैयार किए गए बयान को आपके द्वारा निर्दिष्ट पैरामीटर मानों के साथ जोड़ा जाता है।

यहां महत्वपूर्ण बात यह है कि पैरामीटर मान संकलित कथन के साथ संयुक्त हैं, न कि एसक्यूएल स्ट्रिंग। 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';
}

86
सिर्फ जोड़ने के लिए क्योंकि मैं इसे कहीं और नहीं देख रहा था, रक्षा की एक और पंक्ति एक वेब अनुप्रयोग फ़ायरवॉल (WAF) है जो कि sql इंजेक्शन हमलों को देखने के लिए नियम निर्धारित कर सकती है:
जकारक

37
इसके अलावा, mysql_query का आधिकारिक प्रलेखन केवल एक क्वेरी को निष्पादित करने की अनुमति देता है, इसलिए इसके अलावा कोई अन्य क्वेरी; नजरअंदाज कर दिया है। यहां तक ​​कि अगर यह पहले से ही पदावनत है, तो PHP 5.5.0 के तहत बहुत सारे सिस्टम हैं और यह इस फ़ंक्शन का उपयोग कर सकता है। php.net/manual/en/function.mysql-query.php
Randall Valenciano

12
यह एक बुरी आदत है, लेकिन एक समस्या के बाद का समाधान है: न केवल SQL इंजेक्शन के लिए, बल्कि किसी भी प्रकार के इंजेक्शन के लिए (उदाहरण के लिए F3 फ्रेमवर्क v2 में एक व्यू टेम्प्लेट इंजेक्शन छेद था) यदि आपके पास एक पुरानी वेबसाइट है या ऐप पीड़ित है इंजेक्शन के दोषों से, एक समाधान बूटस्ट्रैप में बच गए मूल्यों के साथ $ _POST जैसे आपके सपेरिगोइड पूर्वनिर्धारित संस्करण के मूल्यों को फिर से असाइन करना है। पीडीओ द्वारा, अभी भी बचना संभव है (आज के ढांचे के लिए भी): पदार्थ ($ pdo-> उद्धरण ($ str, \ PDO :: PARAM_STR), 1, -1)
एलिक्स

15
इस उत्तर में इस बात की व्याख्या का अभाव है कि एक तैयार कथन क्या है - एक बात - यह एक प्रदर्शन हिट है यदि आप अपने अनुरोध के दौरान बहुत सारे तैयार किए गए बयानों का उपयोग करते हैं और कभी-कभी यह 10x प्रदर्शन हिट के लिए खाता है। बेहतर मामला पैरामीटर बाइंडिंग के साथ पीडीओ का उपयोग करेगा, लेकिन स्टेटमेंट तैयारी बंद।
8

6
PDO का उपयोग करना बेहतर है, यदि आप प्रत्यक्ष क्वेरी का उपयोग कर रहे हैं तो सुनिश्चित करें कि आप mysqli का उपयोग करते हैं :: भागने_string
Kassem Itani

1652

पदावनत की गई चेतावनी: इस उत्तर का नमूना कोड (जैसे प्रश्न का नमूना कोड) PHP के MySQLविस्तार का उपयोग करता है , जिसे PHP 5.5.0 में पदावनत किया गया था और पूरी तरह से PHP 7.0.0 में हटा दिया गया था।

सुरक्षा चेतावनी : यह उत्तर सुरक्षा सर्वोत्तम प्रथाओं के अनुरूप नहीं है। एसक्यूएल इंजेक्शन को रोकने के लिए बचने अपर्याप्त है , इसके बजाय तैयार किए गए बयानों का उपयोग करें। अपने जोखिम पर नीचे उल्लिखित रणनीति का उपयोग करें। (इसके अलावा, mysql_real_escape_string()PHP 7 में हटा दिया गया था)

यदि आप PHP के हालिया संस्करण का उपयोग कर रहे हैं, तो mysql_real_escape_stringनीचे उल्लिखित विकल्प अब उपलब्ध नहीं होगा (हालांकि mysqli::escape_stringएक आधुनिक समकक्ष है)। इन दिनों mysql_real_escape_stringविकल्प केवल PHP के पुराने संस्करण पर विरासत कोड के लिए समझ में आएगा।


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

हम पहले वाले भाग से कम प्रभाव वाले स्ट्रिंग को कवर करेंगे।

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

mysql_real_escape_stringफ़ंक्शन का विवरण भी देखें ।

पैरामीटर किए गए क्वेरी का उपयोग करने के लिए, आपको MySQL फ़ंक्शन के बजाय MySQLi का उपयोग करना होगा। अपने उदाहरण को फिर से लिखने के लिए, हमें निम्नलिखित की तरह कुछ की आवश्यकता होगी।

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

आप जिस महत्वपूर्ण कार्य को पढ़ना चाहेंगे, वह वहाँ होगा mysqli::prepare

साथ ही, जैसा कि दूसरों ने सुझाव दिया है, आपको पीडीओ जैसी किसी चीज के साथ अमूर्तता की एक परत को हटाने के लिए उपयोगी / आसान लग सकता है ।

कृपया ध्यान दें कि आपके द्वारा पूछा गया मामला काफी सरल है और अधिक जटिल मामलों में अधिक जटिल दृष्टिकोण की आवश्यकता हो सकती है। विशेष रूप से:

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

1
का उपयोग mysql_real_escape_stringकरना पर्याप्त है या मुझे भी मानकीकृत का उपयोग करना चाहिए?
peiman एफ।

5
@peimanF। एक स्थानीय परियोजना पर भी पैरामीट्रिक प्रश्नों का उपयोग करने का अच्छा अभ्यास रखें। पैराड्राइज्ड प्रश्नों के साथ आपको गारंटी दी जाती है कि एसक्यूएल इंजेक्शन नहीं होगा। लेकिन ध्यान रखें कि आपको htmlentitiesउदाहरण के साथ फर्जी रिट्रीवल (यानी XSS इंजेक्शन, जैसे टेक्स्ट में कोड डालना) से बचने के लिए डेटा को सैनिटाइज करना चाहिए
Goufalite

2
@peimanF। पैराड्राइज्ड क्वेश्चन और बाइंड वैल्यू के लिए अच्छा अभ्यास, लेकिन रियल एस्केप स्ट्रिंग अभी के लिए अच्छा है
रिचर्ड

मैं mysql_real_escape_string()पूर्णता के लिए समावेश को समझता हूं , लेकिन मैं सबसे पहले त्रुटि-प्रवण दृष्टिकोण को सूचीबद्ध करने का प्रशंसक नहीं हूं। पाठक शायद पहला उदाहरण जल्दी से पकड़ सकता है। अच्छी बात यह है कि अब यह
अपदस्थ है

1
@ SteenSchütt - सभी mysql_*कार्यों को पदावनत किया जाता है। उन्हें इसी तरह के mysqli_* कार्यों से बदल दिया गया था , जैसे कि mysqli_real_escape_string
रिक जेम्स

1072

यहां हर उत्तर समस्या का एक हिस्सा है। वास्तव में, चार अलग-अलग क्वेरी भाग हैं जिन्हें हम गतिशील रूप से SQL में जोड़ सकते हैं: -

  • एक स्ट्रिंग
  • एक संख्या
  • एक पहचानकर्ता
  • एक सिंटैक्स कीवर्ड

और तैयार बयान उनमें से केवल दो को कवर करते हैं।

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

सामान्य तौर पर, इस तरह के एक सुरक्षा दृष्टिकोण श्वेतसूची पर आधारित होता है

इस स्थिति में, प्रत्येक गतिशील पैरामीटर को आपकी स्क्रिप्ट में हार्डकोड किया जाना चाहिए और उस सेट से चुना जाना चाहिए। उदाहरण के लिए, डायनेमिक ऑर्डर करने के लिए:

$orders  = array("name", "price", "qty"); // Field names
$key = array_search($_GET['sort'], $orders)); // if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. 
$query = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

इस प्रक्रिया को आसान बनाने के लिए मैंने एक श्वेतसूची सहायक कार्य लिखा, जो एक पंक्ति में सभी काम करता है:

$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name");
$query  = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe

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

फिर भी, SQL सिंटैक्स कीवर्ड (जैसे कि AND, DESCऔर ऐसे) के साथ एक समस्या है , लेकिन इस मामले में श्वेत-सूची एकमात्र दृष्टिकोण है।

तो, एक सामान्य सिफारिश के रूप में प्रकाशित किया जा सकता है

  • कोई भी चर जो एक SQL डेटा शाब्दिक का प्रतिनिधित्व करता है, (या, इसे सीधे शब्दों में कहें - एक SQL स्ट्रिंग, या एक संख्या) को तैयार विवरण के माध्यम से जोड़ा जाना चाहिए। कोई अपवाद नहीं।
  • किसी भी अन्य क्वेरी भाग, जैसे कि SQL कीवर्ड, एक तालिका या फ़ील्ड नाम या एक ऑपरेटर - को एक सफेद सूची के माध्यम से फ़िल्टर किया जाना चाहिए।

अपडेट करें

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

उदाहरण के लिए, वहाँ (1) अभी भी (2) अभी भी (3) कई (4) उत्तर (5) हैं , जिसमें दूसरा सबसे अधिक उत्तर दिया गया है जिसमें आपको मैनुअल स्ट्रिंग भागने का सुझाव दिया गया है - एक पुराना दृष्टिकोण जो असुरक्षित साबित हो रहा है।

या थोड़ा बेहतर उत्तर है जो स्ट्रिंग प्रारूपण का सिर्फ एक और तरीका बताता है और यहां तक ​​कि इसे अंतिम रामबाण के रूप में भी समेटे हुए है। हालांकि, यह नहीं है। यह विधि नियमित स्ट्रिंग प्रारूपण से बेहतर नहीं है, फिर भी यह अपनी सभी कमियां रखता है: यह केवल तार पर लागू होता है और किसी भी अन्य मैनुअल स्वरूपण की तरह, यह अनिवार्य रूप से वैकल्पिक है, गैर-अनिवार्य उपाय, किसी भी प्रकार की मानव त्रुटि के लिए प्रवण है।

मुझे लगता है कि एक बहुत पुराने अंधविश्वास की वजह से यह सब OWASP या PHP मैनुअल जैसे प्राधिकरणों द्वारा समर्थित है , जो कि "भागने" और SQL इंजेक्शन से सुरक्षा के बीच समानता की घोषणा करता है।

भले ही PHP मैनुअल ने उम्र के लिए कहा हो, *_escape_stringकिसी भी तरह से डेटा को सुरक्षित नहीं बनाता है और कभी भी इसका इरादा नहीं किया गया है। स्ट्रिंग के अलावा किसी भी एसक्यूएल भाग के लिए बेकार होने के अलावा, मैनुअल एस्केपिंग गलत है, क्योंकि यह स्वचालित के विपरीत मैनुअल है।

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

इसलिए, जो कुछ भी "भागने" के विपरीत, तैयार किए गए बयान वह उपाय है जो वास्तव में SQL इंजेक्शन (जब लागू हो) से बचाता है।


848

मैं सुझाए गए SQL क्वेरी को चलाने के लिए PDO (PHP डेटा ऑब्जेक्ट) का उपयोग करने की सलाह दूंगा

यह न केवल SQL इंजेक्शन से बचाता है, बल्कि यह प्रश्नों को भी गति देता है।

और बजाय पीडीओ का उपयोग करके mysql_, mysqli_और pgsql_काम करता है, आप अपने आवेदन एक छोटे से, डेटाबेस से अधिक निकाला दुर्लभ घटना आप स्विच डेटाबेस प्रदाताओं के लिए है कि में बनाते हैं।


619

उपयोग करें PDOऔर तैयार प्रश्न।

( $connएक PDOवस्तु है)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

549

जैसा कि आप देख सकते हैं, लोगों का सुझाव है कि आप तैयार किए गए कथनों का उपयोग करें। यह गलत नहीं है, लेकिन जब आपकी क्वेरी को केवल एक बार प्रक्रिया के अनुसार निष्पादित किया जाता है, तो एक मामूली प्रदर्शन जुर्माना होगा।

मैं इस मुद्दे का सामना कर रहा था, लेकिन मुझे लगता है कि मैंने इसे बहुत परिष्कृत तरीके से हल किया - जिस तरह से हैकर्स उद्धरणों का उपयोग करने से बचने के लिए उपयोग करते हैं। मैंने इसका उपयोग अनुकरणीय तैयार कथनों के साथ किया। मैं इसका उपयोग सभी प्रकार के संभावित SQL इंजेक्शन हमलों को रोकने के लिए करता हूं ।

मेरा दृष्टिकोण:

  • यदि आप इनपुट पूर्णांक होने की उम्मीद करते हैं तो सुनिश्चित करें कि यह वास्तव में पूर्णांक है। PHP की तरह एक चर-प्रकार की भाषा में यह बहुत महत्वपूर्ण है। आप उदाहरण के लिए इस बहुत ही सरल लेकिन शक्तिशाली उपाय का उपयोग कर सकते हैं:sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • यदि आप पूर्णांक हेक्स से कुछ और अपेक्षा करते हैं । यदि आप इसे हेक्स करते हैं, तो आप सभी इनपुट से पूरी तरह से बच जाएंगे। C / C ++ में एक फ़ंक्शन होता है mysql_hex_string(), जिसे PHP में आप उपयोग कर सकते हैं bin2hex()

    इस बारे में चिंता न करें कि बची हुई स्ट्रिंग में इसकी मूल लंबाई का 2x आकार होगा क्योंकि यदि आप उपयोग करते हैं mysql_real_escape_string, तो भी PHP को उसी क्षमता को आवंटित करना होगा ((2*input_length)+1), जो समान है।

  • जब आप बाइनरी डेटा ट्रांसफर करते हैं तो यह हेक्स विधि अक्सर उपयोग की जाती है, लेकिन मुझे कोई कारण नहीं दिखता कि SQL इंजेक्शन के हमलों को रोकने के लिए सभी डेटा पर इसका उपयोग क्यों न करें। ध्यान दें कि आपको इसके बजाय 0xMySQL फ़ंक्शन के साथ डेटा को प्रीपेंड करना या उपयोग करना है UNHEX

इसलिए, उदाहरण के लिए, क्वेरी:

SELECT password FROM users WHERE name = 'root'

हो जाएगा:

SELECT password FROM users WHERE name = 0x726f6f74

या

SELECT password FROM users WHERE name = UNHEX('726f6f74')

हेक्स एकदम सही बच रहा है। इंजेक्शन लगाने का कोई तरीका नहीं।

UNHEX फ़ंक्शन और 0x उपसर्ग के बीच अंतर

टिप्पणियों में कुछ चर्चा हुई थी, इसलिए मैं अंत में इसे स्पष्ट करना चाहता हूं। ये दोनों दृष्टिकोण बहुत समान हैं, लेकिन वे कुछ मायनों में अलग हैं:

** 0x ** उपसर्ग का उपयोग केवल डेटा कॉलम जैसे कि चार, वर्चर, टेक्स्ट, ब्लॉक, बाइनरी, आदि के लिए किया जा सकता है
इसके अलावा, यदि आप एक खाली स्ट्रिंग सम्मिलित करने वाले हैं तो इसका उपयोग थोड़ा जटिल है। आपको इसे पूरी तरह से बदलना होगा '', या आपको एक त्रुटि मिलेगी।

UNHEX () किसी भी कॉलम पर काम करता है ; आपको खाली स्ट्रिंग के बारे में चिंता करने की आवश्यकता नहीं है।


हेक्स विधियों को अक्सर हमलों के रूप में उपयोग किया जाता है

ध्यान दें कि इस हेक्स विधि का उपयोग अक्सर SQL इंजेक्शन हमले के रूप में किया जाता है जहां पूर्णांक तार की तरह होते हैं और बस के साथ भाग जाते हैं mysql_real_escape_string। तब आप उद्धरण के उपयोग से बच सकते हैं।

उदाहरण के लिए, यदि आप ऐसा कुछ करते हैं:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

एक हमला आपको बहुत आसानी से इंजेक्ट कर सकता है । अपनी स्क्रिप्ट से दिए गए निम्न इंजेक्शन कोड पर विचार करें:

चयन करें ... जहां id = -1 संघ है, सभी का चयन करें table_name जानकारी_schema.tables से करें

और अब सिर्फ टेबल संरचना निकालें:

चयन करें ... कहाँ आईडी = -1 संघ सभी का चयन करें column_name जानकारी_schema.column से, जहाँ table_name = 0x61727469636c65

और फिर जो भी डाटा चाहिए उसे सेलेक्ट करें। क्या यह अच्छा नहीं है?

लेकिन अगर किसी इंजेक्टेबल साइट का कोडर हेक्स करेगा, तो कोई इंजेक्शन संभव नहीं होगा क्योंकि क्वेरी इस तरह दिखेगी: SELECT ... WHERE id = UNHEX('2d312075...3635')


6
@ सुमितगुप्त ये, आपने किया। MySQL के साथ +लेकिन साथ नहीं है CONCAT। और प्रदर्शन करने के लिए: मुझे नहीं लगता कि यह प्रदर्शन को प्रभावित करता है क्योंकि mysql को डेटा पार्स करना पड़ता है और अगर यह मूल या स्ट्रिंग है तो कोई फर्क नहीं पड़ता
Zaffy

11
@YourCommonSense आप इस अवधारणा को नहीं समझते हैं ... यदि आप mysql में स्ट्रिंग करना चाहते हैं, तो आप इसे इस तरह से उद्धृत 'root'कर सकते हैं या 0x726f6f74यदि आप एक नंबर चाहते हैं तो इसे BUT कर सकते हैं और इसे स्ट्रिंग के रूप में भेज सकते हैं, आप शायद '42 'लिखेंगे CHAR (42) ) ... हेक्स में '42' 0x3432नहीं होगा0x42
ज़ाफ

7
@YourCommonSense मेरे पास कहने के लिए कुछ भी नहीं है ... सिर्फ लोल ... यदि आप अभी भी संख्यात्मक क्षेत्रों पर हेक्स की कोशिश करना चाहते हैं, तो दूसरी टिप्पणी देखें। मैं तुम्हारे साथ शर्त लगाता हूं कि यह काम करेगा।
जूली

2
उत्तर स्पष्ट रूप से बताता है कि यह पूर्णांक मानों के साथ उस तरह से काम नहीं करेगा, इसका कारण यह है कि Bin2hex एक स्ट्रिंग के लिए दिए गए मान को परिवर्तित करता है (और इस प्रकार bin2hex (0) 0x30 है, और 0x03 नहीं) - शायद यही वह भाग है जो आपको भ्रमित करता है । यदि आप इसका अनुसरण करते हैं, तो यह पूरी तरह से काम करता है (कम से कम मेरी साइट पर, 4 अलग-अलग mysql संस्करणों के साथ डेबियन मशीनों पर परीक्षण किया गया, 5.1.x से 5.6.x)। आखिरकार, हेक्साडेसिमल केवल प्रतिनिधित्व का तरीका है, मूल्य नहीं;)
ग्रिफिन

5
@YourCommonSense आप अभी भी नहीं समझते हैं? आप 0x और concat का उपयोग नहीं कर सकते क्योंकि यदि स्ट्रिंग खाली है तो आप एक त्रुटि के साथ समाप्त हो जाएंगे। यदि आप अपनी क्वेरी के लिए सरल विकल्प चाहते हैं, तो यह एक कोशिश करेंSELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ')
Zaffy

499

पदावनत की गई चेतावनी: इस उत्तर का नमूना कोड (जैसे प्रश्न का नमूना कोड) PHP के MySQLविस्तार का उपयोग करता है , जिसे PHP 5.5.0 में पदावनत किया गया था और पूरी तरह से PHP 7.0.0 में हटा दिया गया था।

सुरक्षा चेतावनी : यह उत्तर सुरक्षा सर्वोत्तम प्रथाओं के अनुरूप नहीं है। एसक्यूएल इंजेक्शन को रोकने के लिए बचने अपर्याप्त है , इसके बजाय तैयार किए गए बयानों का उपयोग करें। अपने जोखिम पर नीचे उल्लिखित रणनीति का उपयोग करें। (इसके अलावा, mysql_real_escape_string()PHP 7 में हटा दिया गया था)

जरूरी

SQL इंजेक्शन को रोकने के लिए सबसे अच्छा तरीका है का उपयोग है तैयार बयान के बजाय बचने का रूप, स्वीकार किए जाते हैं जवाब को दर्शाता है।

Aura.Sql और EasyDB जैसे पुस्तकालय हैं जो डेवलपर्स को तैयार किए गए कथनों का आसान उपयोग करने की अनुमति देते हैं। SQL इंजेक्शन को रोकने के लिए तैयार किए गए कथन बेहतर क्यों हैं, इस बारे में अधिक जानने के लिए , इस mysql_real_escape_string()बायपास का संदर्भ लें और हाल ही में वर्डप्रेस में यूनिकोड एसक्यूएल इंजेक्शन कमजोरियों को निर्धारित करें

इंजेक्शन की रोकथाम - mysql_real_escape_string ()

इन हमलों को रोकने के लिए PHP का एक विशेष रूप से बनाया गया कार्य है। तुम सब करने की ज़रूरत है, एक समारोह का कौर का उपयोग है mysql_real_escape_string

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

नोट: आप इस समारोह का उपयोग करने के लिए डेटाबेस से जुड़ा होना चाहिए!

// MySQL से कनेक्ट करें

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

आप MySQL में अधिक विवरण पा सकते हैं - SQL इंजेक्शन निवारण


30
यह सबसे अच्छा है जो आप विरासत mysql एक्सटेंशन के साथ कर सकते हैं। नए कोड के लिए, आपको mysqli या PDO पर स्विच करने की सलाह दी जाती है।
अल्वारो गोंजालेज

7
मैं इस 'इन हमलों को रोकने के लिए एक विशेष रूप से बनाए गए समारोह' से सहमत नहीं हूं। मुझे लगता है कि mysql_real_escape_stringउद्देश्य हर इनपुट डेटा-स्ट्रिंग के लिए सही SQL क्वेरी बनाने की अनुमति है। रोकथाम एसक्यूएल-इंजेक्शन इस फ़ंक्शन का साइड-इफेक्ट है।
संप्रदाय

4
आप सही इनपुट डेटा-स्ट्रिंग्स लिखने के लिए फ़ंक्शन का उपयोग नहीं करते हैं। आप सिर्फ सही लिखते हैं जिन्हें भागने की जरूरत नहीं है या पहले ही बच गए हैं। mysql_real_escape_string () आपके द्वारा उल्लेखित उद्देश्य से डिज़ाइन किया गया हो सकता है, लेकिन इसका एकमात्र मूल्य इंजेक्शन को रोकना है।
नाज़का

17
चेतावनी! mysql_real_escape_string() अचूक नहीं है
अहग्याल

9
mysql_real_escape_stringअब पदावनत कर दिया गया है, इसलिए इसका अब व्यवहार्य विकल्प नहीं है। इसे भविष्य में PHP से हटा दिया जाएगा। PHP या MySQL लोग क्या सलाह देते हैं, इस पर आगे बढ़ना।
jww

461

आप इस तरह से कुछ बुनियादी कर सकते हैं:

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

यह हर समस्या को हल नहीं करेगा, लेकिन यह एक बहुत अच्छा कदम है। मैंने स्पष्ट वस्तुओं को छोड़ दिया जैसे कि चर के अस्तित्व, प्रारूप (संख्या, अक्षर, आदि) की जांच करना।


28
मैंने आपके उदाहरण की कोशिश की है और यह मेरे लिए ठीक है। क्या आप स्पष्ट हैं कि "यह हर समस्या का समाधान नहीं करेगा"
चिनूक

14
यदि आप स्ट्रिंग को उद्धृत नहीं करते हैं, तो यह अभी भी इंजेक्शन योग्य है। $q = "SELECT col FROM tbl WHERE x = $safe_var";उदाहरण के लिए लें । स्थापना $safe_varके लिए 1 UNION SELECT password FROM usersउद्धरण की कमी की वजह इस मामले में काम करता है। उपयोग करके क्वेरी में स्ट्रिंग्स को इंजेक्ट करना भी संभव है CONCATऔर CHR
बहुपद

4
@Polynomial पूरी तरह से सही है, लेकिन मैं इसे केवल गलत उपयोग के रूप में देखूंगा। जब तक आप इसे सही तरीके से उपयोग करते हैं, यह निश्चित रूप से काम करेगा।
ग्लॉगल

22
चेतावनी! mysql_real_escape_string() अचूक नहीं है

8
mysql_real_escape_stringअब पदावनत कर दिया गया है, इसलिए इसका अब व्यवहार्य विकल्प नहीं है। इसे भविष्य में PHP से हटा दिया जाएगा। PHP या MySQL लोग क्या सलाह देते हैं, इस पर आगे बढ़ना।
jww

380

जो कुछ भी आप का उपयोग करते हुए समाप्त करते हैं, सुनिश्चित करें कि आप जांचते हैं कि आपका इनपुट पहले से ही magic_quotesया कुछ अन्य अच्छी तरह से बकवास नहीं है, और यदि आवश्यक हो, तो इसे stripslashesया जो भी इसे पवित्र करना है उसे चलाएं ।


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

24
PHP 5.4 के रूप में, 'मैजिक कोट्स' के रूप में ज्ञात घृणा को मार दिया गया है । और बुरे उबटन के लिए अच्छी लकीर।
ब्रायनह

363

पदावनत की गई चेतावनी: इस उत्तर का नमूना कोड (जैसे प्रश्न का नमूना कोड) PHP के MySQLविस्तार का उपयोग करता है , जिसे PHP 5.5.0 में पदावनत किया गया था और पूरी तरह से PHP 7.0.0 में हटा दिया गया था।

सुरक्षा चेतावनी : यह उत्तर सुरक्षा सर्वोत्तम प्रथाओं के अनुरूप नहीं है। एसक्यूएल इंजेक्शन को रोकने के लिए बचने अपर्याप्त है , इसके बजाय तैयार किए गए बयानों का उपयोग करें। अपने जोखिम पर नीचे उल्लिखित रणनीति का उपयोग करें। (इसके अलावा, mysql_real_escape_string()PHP 7 में हटा दिया गया था)

Parameterized क्वेरी और इनपुट सत्यापन जाने का रास्ता है। ऐसे कई परिदृश्य हैं जिनके तहत SQL इंजेक्शन का उपयोग किया जा सकता है, भले ही mysql_real_escape_string()इसका उपयोग किया गया हो।

वे उदाहरण SQL इंजेक्शन के लिए असुरक्षित हैं:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

या

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

दोनों मामलों में, आप 'इनकैप्सुलेशन की सुरक्षा के लिए उपयोग नहीं कर सकते ।

स्रोत : अनपेक्षित SQL इंजेक्शन (जब बचने के लिए पर्याप्त नहीं है)


2
यदि आप इनपुट सत्यापन तकनीक को अपनाते हैं तो आप SQL इंजेक्शन को रोक सकते हैं जिसमें उपयोगकर्ता इनपुट लंबाई, प्रकार और वाक्यविन्यास के लिए परिभाषित नियमों के एक सेट के खिलाफ और व्यावसायिक नियमों के खिलाफ भी प्रमाणित होता है।
जोसिप आइविक

312

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

MVC पैटर्न और CakePHP या CodeIgniter जैसे ढांचे को अपनाना शायद जाने का सही तरीका है: सुरक्षित डेटाबेस क्वेरी बनाने जैसे सामान्य कार्यों को हल किया गया है और इस तरह के फ्रेमवर्क में इन्हें लागू किया गया है। वे आपको अपने वेब एप्लिकेशन को एक समझदार तरीके से व्यवस्थित करने में मदद करते हैं और सुरक्षित रूप से एकल SQL प्रश्नों के निर्माण की तुलना में वस्तुओं को लोड करने और सहेजने के बारे में अधिक सोचते हैं।


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

@AnthonyRutledge आप बिल्कुल सही हैं। यह समझना बहुत महत्वपूर्ण है कि क्या चल रहा है और क्यों। हालाँकि, मौका है कि एक सच्चे और कोशिश की और सक्रिय रूप से इस्तेमाल किया और विकसित फ्रेमवर्क में चला गया है और बहुत सारे मुद्दों को हल किया है और पहले से ही बहुत अधिक सुरक्षा छेद खड़ा किया है। कोड गुणवत्ता के लिए एक स्रोत प्राप्त करने के लिए स्रोत को देखना एक अच्छा विचार है। यदि यह एक अप्रयुक्त गड़बड़ है तो यह संभवतः सुरक्षित नहीं है।
जोहान्स फारेनक्रग

3
यहाँ। यहाँ। अच्छे अंक। हालाँकि, क्या आप इस बात से सहमत होंगे कि बहुत से लोग MVC प्रणाली को अपनाने के लिए अध्ययन कर सकते हैं और सीख सकते हैं, लेकिन हर कोई इसे हाथ से नियंत्रित नहीं कर सकता (नियंत्रक और सर्वर)। इस बिंदु के साथ बहुत दूर जा सकते हैं। क्या मुझे अपना मूंगफली का मक्खन पेकन कुकीज़ गर्म करने से पहले मेरे माइक्रोवेव को समझने की जरूरत है? ;-)
एंथनी रटलेज

3
@AnthonyRutledge मैं सहमत हूँ! मुझे लगता है कि उपयोग-मामले में भी अंतर होता है: क्या मैं अपने व्यक्तिगत मुखपृष्ठ के लिए एक फोटो गैलरी का निर्माण कर रहा हूं या क्या मैं एक ऑनलाइन बैंकिंग वेब एप्लिकेशन का निर्माण कर रहा हूं? बाद के मामले में सुरक्षा के विवरणों को समझना बहुत महत्वपूर्ण है और जो एक फ्रेमवर्क मैं उपयोग कर रहा हूं, उन्हें संबोधित कर रहा हूं।
जोहान्स फारेनक्रग

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

298

मैं संग्रहीत प्रक्रियाओं का पक्ष लेता हूं ( सुरक्षा के दृष्टिकोण से MySQL ने 5.0 से समर्थन प्रक्रियाएं संग्रहीत की हैं ) - फायदे हैं -

  1. अधिकांश डेटाबेस ( MySQL सहित ) उपयोगकर्ता पहुंच को संग्रहीत प्रक्रियाओं को निष्पादित करने के लिए प्रतिबंधित करने में सक्षम बनाते हैं। विशेषाधिकारों के हमलों की वृद्धि को रोकने के लिए ठीक-ठीक सुरक्षा पहुँच नियंत्रण उपयोगी है। यह डेटाबेस से सीधे SQL चलाने में सक्षम होने से समझौता किए गए एप्लिकेशन को रोकता है।
  2. वे अनुप्रयोग से कच्ची SQL क्वेरी का सार करते हैं इसलिए डेटाबेस संरचना की कम जानकारी अनुप्रयोग के लिए उपलब्ध है। यह लोगों को डेटाबेस की अंतर्निहित संरचना को समझने और उपयुक्त हमलों को डिजाइन करने के लिए कठिन बनाता है।
  3. वे केवल मापदंडों को स्वीकार करते हैं, इसलिए पैरामीटर किए गए प्रश्नों के फायदे हैं। बेशक - IMO आपको अभी भी अपने इनपुट को सैनिटाइज करने की आवश्यकता है - खासकर यदि आप संग्रहीत प्रक्रिया के अंदर गतिशील एसक्यूएल का उपयोग कर रहे हैं।

नुकसान हैं -

  1. वे (संग्रहीत प्रक्रियाएं) बनाए रखने के लिए कठिन हैं और बहुत जल्दी गुणा करते हैं। यह उन्हें एक मुद्दा बनाता है।
  2. वे गतिशील प्रश्नों के लिए बहुत उपयुक्त नहीं हैं - अगर उन्हें डायनामिक कोड को मापदंडों के रूप में स्वीकार करने के लिए बनाया गया है, तो बहुत सारे फायदे नकारात्मक हैं।

297

SQL इंजेक्शन और अन्य SQL हैक को रोकने के कई तरीके हैं। आप इसे इंटरनेट (Google खोज) पर आसानी से पा सकते हैं। बेशक पीडीओ अच्छे समाधानों में से एक है। लेकिन मैं आपको SQL इंजेक्शन से कुछ अच्छे लिंक रोकथाम का सुझाव देना चाहूंगा।

SQL इंजेक्शन क्या है और इसे कैसे रोका जाए

SQL इंजेक्शन के लिए PHP मैनुअल

PHP में SQL इंजेक्शन और रोकथाम का Microsoft स्पष्टीकरण

और कुछ अन्य जैसे MySQL और PHP के साथ SQL इंजेक्शन को रोकना

अब, आपको SQL इंजेक्शन से अपनी क्वेरी को रोकने की आवश्यकता क्यों है?

मैं आपको बताना चाहता हूं: हम नीचे दिए छोटे उदाहरण के साथ SQL इंजेक्शन को रोकने के लिए प्रयास क्यों करते हैं:

लॉगिन प्रमाणीकरण मैच के लिए प्रश्न:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

अब, अगर कोई (एक हैकर) डालता है

$_POST['email']= admin@emali.com' OR '1=1

और पासवर्ड कुछ भी…।

क्वेरी को केवल सिस्टम में पार्स किया जाएगा:

$query="select * from users where email='admin@emali.com' OR '1=1';

दूसरे भाग को छोड़ दिया जाएगा। तो, क्या होगा? एक गैर-अधिकृत उपयोगकर्ता (हैकर) अपने पासवर्ड के बिना प्रशासक के रूप में लॉग इन कर सकेगा। अब, वह / वह कुछ भी कर सकता है जो प्रशासक / ईमेल व्यक्ति कर सकता है। देखें, यदि SQL इंजेक्शन को रोका नहीं जाता है तो यह बहुत खतरनाक है।


267

मुझे लगता है कि अगर कोई PHP और MySQL या किसी अन्य डेटाबेस सर्वर का उपयोग करना चाहता है:

  1. पीडीओ (PHP डेटा ऑब्जेक्ट्स) सीखने के बारे में सोचें - यह एक डेटाबेस एक्सेस लेयर है जो कई डेटाबेस तक पहुँच की एक समान विधि प्रदान करता है।
  2. MySQLi सीखने के बारे में सोचें
  3. मूल PHP फ़ंक्शंस का उपयोग करें: जैसे: strip_tags , mysql_real_escape_string या यदि वैरिएबल संख्यात्मक, बस (int)$foo। PHP में चर के प्रकार के बारे में अधिक पढ़ें यहाँ । यदि आप PDO या MySQLi जैसी लाइब्रेरी का उपयोग कर रहे हैं, तो हमेशा PDO :: उद्धरण () और mysqli_real_escape_string () का उपयोग करें

पुस्तकालय उदाहरण:

---- पीडीओ

----- नहीं प्लेसहोल्डर - SQL इंजेक्शन के लिए पका! यह बुरा है

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- प्लेसहोल्डर

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- नामित प्लेसहोल्डर

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

पुनश्च :

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

लेकिन जब PDO और MySQLi दोनों काफी तेज होते हैं, तो MySQLi बेंचमार्क में बेहिसाब तेजी से प्रदर्शन करता है - ~ गैर-तैयार बयानों के लिए 2.5%, और तैयार लोगों के लिए ~ 6.5%।

और कृपया अपने डेटाबेस की हर क्वेरी का परीक्षण करें - यह इंजेक्शन को रोकने का एक बेहतर तरीका है।


कि mysqli गलत है। पहला परम डेटा प्रकार व्यक्त करता है।
मिकमैकुसा

257

यदि संभव हो, तो अपने मापदंडों के प्रकार डालें। लेकिन यह केवल इंट, बूल और फ्लोट जैसे सरल प्रकारों पर काम कर रहा है।

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

3
यह उन कुछ मामलों में से एक है जहां मैं एक तैयार किए गए बयान के बजाय "बच गए मूल्य" का उपयोग करेगा। और पूर्णांक प्रकार रूपांतरण अत्यंत कुशल है।
होल्डऑफहंगर

233

आप कैश इंजन का लाभ लेने के चाहते हैं, जैसे Redis या Memcached , हो सकता है DALMP एक विकल्प हो सकता है। यह शुद्ध MySQLi का उपयोग करता है । यह जांचें: PHP का उपयोग करके MySQL के लिए DALMP डेटाबेस एब्स्ट्रक्शन लेयर।

इसके अलावा, आप अपनी क्वेरी तैयार करने से पहले अपनी दलीलें 'तैयार' कर सकते हैं ताकि आप गतिशील क्वेरी बना सकें और अंत में पूरी तरह से तैयार स्टेटमेंट क्वेरी हो। PHP का उपयोग करके MySQL के लिए DALMP Database Abstraction Layer।


224

पीडीओ ( mysql_कार्यों से आने वाले ) का उपयोग करने के बारे में अनिश्चितता के लिए , मैंने एक बहुत, बहुत ही सरल पीडीओ आवरण बनाया है जो एक एकल फ़ाइल है। यह दिखाने के लिए मौजूद है कि सभी सामान्य चीजों को करना कितना आसान है, इसे करने की आवश्यकता है। PostgreSQL, MySQL और SQLite के साथ काम करता है।

मूल रूप से, इसे पढ़ते समय आप मैनुअल को पढ़ सकते हैं कि वास्तविक जीवन में उपयोग करने के लिए पीडीओ कार्यों को कैसे रखा जाए, जो आप चाहते हैं उस प्रारूप में मूल्यों को संग्रहीत और पुनर्प्राप्त करने के लिए वास्तविक जीवन में उपयोग करें ।

मुझे एक ही कॉलम चाहिए

$count = DB::column('SELECT COUNT(*) FROM `user`);

मुझे एक सरणी (कुंजी => मान) परिणाम चाहिए (यानी एक चयन बॉक्स बनाने के लिए)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

मुझे एकल पंक्ति परिणाम चाहिए

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

मुझे परिणामों की एक सरणी चाहिए

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

222

इस PHP फ़ंक्शन का उपयोग करके mysql_escape_string()आप एक तेज़ तरीके से एक अच्छी रोकथाम प्राप्त कर सकते हैं।

उदाहरण के लिए:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - mysql_query में उपयोग के लिए एक स्ट्रिंग से बच जाता है

अधिक रोकथाम के लिए, आप अंत में जोड़ सकते हैं ...

wHERE 1=1   or  LIMIT 1

अंत में आपको मिलता है:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

220

एसक्यूएल बयानों में विशेष पात्रों से बचने के लिए कुछ दिशानिर्देश।

MySQL का उपयोग न करें । यह विस्तार पदावनत है। इसके बजाय MySQLi या PDO का उपयोग करें ।

MySQLi

स्ट्रिंग में विशेष वर्णों को मैन्युअल रूप से भागने के लिए आप mysqli_real_escape_string फ़ंक्शन का उपयोग कर सकते हैं । जब तक सही चरित्र सेट mysqli_set_charset के साथ सेट नहीं किया जाता है, तब तक फ़ंक्शन ठीक से काम नहीं करेगा ।

उदाहरण:

$mysqli = new mysqli('host', 'user', 'password', 'database');
$mysqli->set_charset('charset');

$string = $mysqli->real_escape_string($string);
$mysqli->query("INSERT INTO table (column) VALUES ('$string')");

तैयार किए गए कथनों के साथ मूल्यों के स्वत: बचने के लिए, mysqli_prepare और mysqli_stmt_bind_param का उपयोग करें , जहां संगत बाइंड चर के लिए प्रकार एक उचित रूपांतरण के लिए प्रदान किया जाना चाहिए:

उदाहरण:

$stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)");

$stmt->bind_param("is", $integer, $string);

$stmt->execute();

कोई बात नहीं अगर आप तैयार बयानों का उपयोग करते हैं या mysqli_real_escape_string, आपको हमेशा यह जानना होगा कि आप किस प्रकार के इनपुट डेटा के साथ काम कर रहे हैं।

इसलिए यदि आप एक तैयार कथन का उपयोग करते हैं, तो आपको mysqli_stmt_bind_paramफ़ंक्शन के प्रकारों को निर्दिष्ट करना होगा ।

और उपयोग के mysqli_real_escape_stringलिए है, जैसा कि नाम कहता है, एक स्ट्रिंग में विशेष वर्णों से बच रहा है, इसलिए यह पूर्णांकों को सुरक्षित नहीं करेगा। इस फ़ंक्शन का उद्देश्य एसक्यूएल स्टेटमेंट्स में स्ट्रिंग्स को तोड़ने से रोकना है, और डेटाबेस को नुकसान हो सकता है जो इसका कारण बन सकता है। mysqli_real_escape_stringएक उपयोगी कार्य है जब ठीक से उपयोग किया जाता है, खासकर जब संयुक्त के साथ sprintf

उदाहरण:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer);

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf("SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer);

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

3
प्रश्न बहुत ही सामान्य है। ऊपर कुछ महान जवाब, लेकिन अधिकांश तैयार किए गए सुझावों का सुझाव देते हैं। MySQLi async तैयार कथनों का समर्थन नहीं करता है, इसलिए स्प्रिंटफ इस स्थिति के लिए एक बढ़िया विकल्प की तरह दिखता है।
डस्टिन ग्राहम

183

इस समस्या का सरल विकल्प डेटाबेस में ही उचित अनुमति देकर हल किया जा सकता है। उदाहरण के लिए: यदि आप MySQL डेटाबेस का उपयोग कर रहे हैं तो टर्मिनल या यूआई के माध्यम से डेटाबेस में प्रवेश करें और इस कमांड का पालन करें:

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

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

FLUSH PRIVILEGES; 

फ्लश के बारे में अधिक जानकारी ।

उपयोगकर्ता के लिए वर्तमान विशेषाधिकारों को देखने के लिए निम्नलिखित क्वेरी को फायर करें।

select * from mysql.user where User='username';

GRANT के बारे में और जानें ।


25
यह उत्तर अनिवार्य रूप से गलत है , क्योंकि यह एक इंजेक्शन की रोकथाम को रोकने में मदद नहीं करता है , लेकिन सिर्फ परिणामों को नरम करने की कोशिश कर रहा है। व्यर्थ में।
आपका कॉमन सेंस

1
ठीक है, यह एक समाधान प्रदान नहीं करता है, लेकिन चीजों से बचने के लिए आप हाथ से पहले क्या कर सकते हैं।
अपूर्वा नेरलेकर

1
@ अपूर्वा यदि मेरा लक्ष्य आपके डेटाबेस से निजी जानकारी पढ़ना है, तो DELETE अनुमति नहीं होने का मतलब कुछ भी नहीं है।
एलेक्स हॉल्ग्रोव

1
@AlexHolsgrove: यह आसान ले लो, मैं सिर्फ परिणामों को नरम करने के लिए अच्छी प्रथाओं का सुझाव दे रहा था।
अपूर्वा नेरलेकर

2
@ अपूर्व आप "परिणामों को नरम करना" नहीं चाहते हैं, आप इसके खिलाफ सुरक्षा के लिए हर संभव प्रयास करना चाहते हैं। हालांकि निष्पक्ष होना, सही उपयोगकर्ता पहुंच स्थापित करना महत्वपूर्ण है, लेकिन वास्तव में ओपी क्या पूछ रहा है।
एलेक्स होल्ग्रोव

177

कई उपयोगी जवाबों के बारे में, मुझे इस धागे में कुछ मूल्य जोड़ने की उम्मीद है।

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

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

SQL इंजेक्शन के प्रति मेरा दृष्टिकोण है: डेटाबेस में भेजने से पहले उपयोगकर्ता-इनपुट डेटा को साफ़ करना (किसी भी क्वेरी के अंदर उपयोग करने से पहले)।

के लिए डेटा फ़िल्टरिंग (असुरक्षित डेटा को सुरक्षित डेटा में परिवर्तित करना)

गौर करें कि PDO और MySQLi उपलब्ध नहीं हैं। आप अपने आवेदन को कैसे सुरक्षित कर सकते हैं? क्या आप मुझे उनका उपयोग करने के लिए मजबूर करते हैं? PHP के अलावा अन्य भाषाओं के बारे में क्या? मैं सामान्य विचारों को प्रदान करना पसंद करता हूं क्योंकि इसका उपयोग व्यापक सीमा के लिए किया जा सकता है, न कि केवल एक विशिष्ट भाषा के लिए।

  1. SQL उपयोगकर्ता (उपयोगकर्ता विशेषाधिकार को सीमित करना): सबसे आम SQL ऑपरेशन हैं (SELECT, UPDATE, INSERT), फिर, UPDATE को उस उपयोगकर्ता को विशेषाधिकार क्यों दें जिसकी आवश्यकता नहीं है? उदाहरण के लिए, लॉगिन और खोज पृष्ठ केवल SELECT का उपयोग कर रहे हैं, फिर, इन पृष्ठों में DB उपयोगकर्ताओं को उच्च विशेषाधिकार के साथ क्यों उपयोग करें?

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

कम से कम विशेषाधिकार का सिद्धांत देखें ।

  1. डेटा फ़िल्टरिंग: किसी भी क्वेरी उपयोगकर्ता इनपुट के निर्माण से पहले, इसे मान्य और फ़िल्टर किया जाना चाहिए। प्रोग्रामर के लिए, प्रत्येक उपयोगकर्ता-इनपुट चर के लिए कुछ गुणों को परिभाषित करना महत्वपूर्ण है: डेटा प्रकार, डेटा पैटर्न और डेटा लंबाई । एक फ़ील्ड जो (x और y) के बीच की संख्या है, सटीक नियम का उपयोग करके बिल्कुल मान्य होनी चाहिए, और एक फ़ील्ड के लिए जो एक स्ट्रिंग (पाठ) है: पैटर्न मामला है, उदाहरण के लिए, एक उपयोगकर्ता नाम में केवल कुछ वर्ण होने चाहिए, आइए [a-zA-Z0-9_-] कहें। लंबाई (x और n) के बीच भिन्न होती है जहां x और n (पूर्णांक, x <= n)। नियम: सटीक फ़िल्टर और सत्यापन नियम बनाना मेरे लिए सर्वोत्तम अभ्यास हैं।

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

अधिक जानकारी के लिए, कृपया OWASP SQL इंजेक्शन निवारण धोखा शीट पढ़ें ।

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

अंत में, आइए विचार करें कि कोई उपयोगकर्ता अपने उपयोगकर्ता नाम दर्ज करने के बजाय नीचे यह पाठ भेजता है:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

इस इनपुट को बिना किसी तैयार किए गए स्टेटमेंट और स्टोर की गई प्रक्रियाओं के बिना जल्दी से चेक किया जा सकता है, लेकिन यूजर-डेटा फ़िल्टरिंग और सत्यापन के बाद उनका उपयोग करना सुरक्षित पक्ष पर होना चाहिए।

अंतिम बिंदु अप्रत्याशित व्यवहार का पता लगा रहा है जिसके लिए अधिक प्रयास और जटिलता की आवश्यकता होती है; यह सामान्य वेब अनुप्रयोगों के लिए अनुशंसित नहीं है।

उपरोक्त उपयोगकर्ता इनपुट में अप्रत्याशित व्यवहार SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, और रूट है। एक बार जब इन शब्दों का पता चला, तो आप इनपुट से बच सकते हैं।

अद्यतन 1:

एक उपयोगकर्ता ने टिप्पणी की कि यह पोस्ट बेकार है, ठीक है! यहाँ OWASP.ORG प्रदान किया गया है :

प्राथमिक बचाव:

विकल्प # 1: तैयार किए गए विवरणों का उपयोग (परिमित क्वेरीज़)
विकल्प # 2: संग्रहीत कार्यविधियाँ
विकल्प # 3 का उपयोग: सभी उपयोगकर्ता अनुपूरित इनपुट से बचना

अतिरिक्त बचाव:

इसके अलावा लागू करें: कम से कम विशेषाधिकार
भी निष्पादित करें: श्वेत सूची इनपुट सत्यापन

जैसा कि आप जानते हैं, एक लेख का दावा करना वैध तर्क द्वारा समर्थित होना चाहिए, कम से कम एक संदर्भ द्वारा! अन्यथा, यह एक हमला और एक बुरा दावा माना जाता है!

अपडेट 2:

PHP मैनुअल से, PHP: तैयार विवरण - मैनुअल :

बचने और एसक्यूएल इंजेक्शन

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

SQL इंजेक्शन को रोकने के लिए सर्वर के भीतर मानों का स्वचालित रूप से बचना कभी-कभी एक सुरक्षा विशेषता माना जाता है। गैर-तैयार बयानों के साथ सुरक्षा का एक ही अंश प्राप्त किया जा सकता है यदि इनपुट मान सही ढंग से बच गए हैं।

अपडेट 3:

मैंने पीडीओ और MySQLi ने तैयार स्टेटमेंट का उपयोग करते समय MySQL सर्वर को क्वेरी कैसे भेजते हैं, यह जानने के लिए टेस्ट केस बनाए:

पीडीओ:

$user = "''1''"; // Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

क्वेरी लॉग:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

MySQLi:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

क्वेरी लॉग:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

यह स्पष्ट है कि एक तैयार बयान भी डेटा से बच रहा है, और कुछ नहीं।

जैसा कि उपरोक्त कथन में भी बताया गया है,

SQL इंजेक्शन को रोकने के लिए सर्वर के भीतर मानों का स्वचालित रूप से बचना कभी-कभी एक सुरक्षा विशेषता माना जाता है। गैर-तैयार बयानों के साथ सुरक्षा की एक ही डिग्री प्राप्त की जा सकती है, यदि इनपुट मान सही ढंग से बच गए हैं

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

कृपया इस प्रश्न को और अधिक विस्तार से देखें: PDO MySQL को कच्ची क्वेरी भेजता है जबकि मैसकली तैयार क्वेरी भेजता है, दोनों एक ही परिणाम उत्पन्न करते हैं

संदर्भ:

  1. SQL इंजेक्शन धोखा शीट
  2. एसक्यूएल इंजेक्षन
  3. सूचना सुरक्षा
  4. सुरक्षा सिद्धांत
  5. डेटा मान्य

175

सुरक्षा चेतावनी : यह उत्तर सुरक्षा सर्वोत्तम प्रथाओं के अनुरूप नहीं है। एसक्यूएल इंजेक्शन को रोकने के लिए बचने अपर्याप्त है , इसके बजाय तैयार किए गए बयानों का उपयोग करें। अपने जोखिम पर नीचे उल्लिखित रणनीति का उपयोग करें। (इसके अलावा, mysql_real_escape_string()PHP 7 में हटा दिया गया था)

पदावनत की गई चेतावनी : इस समय mysql एक्सटेंशन को हटा दिया गया है। हम पीडीओ एक्सटेंशन का उपयोग करने की सलाह देते हैं

मैं अपने वेब एप्लिकेशन को SQL इंजेक्शन की चपेट में आने से रोकने के लिए तीन अलग-अलग तरीकों का उपयोग करता हूं।

  1. उपयोग की mysql_real_escape_string(), में एक पूर्व निर्धारित समारोह है जो पीएचपी , और निम्न वर्ण के लिए इस कोड ऐड बैकस्लैश: \x00, \n, \r, \, ', "और \x1a। SQL इंजेक्शन की संभावना को कम करने के लिए पैरामीटर के रूप में इनपुट मानों को पास करें।
  2. सबसे उन्नत तरीका पीडीओ का उपयोग करना है।

उम्मीद है इससे आपको मदद मिलेगी।

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

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string () यहां सुरक्षा नहीं करेगा। यदि आप अपने क्वेरी के अंदर अपने चर के आसपास सिंगल कोट्स ('') का उपयोग करते हैं, तो इससे आपको क्या सुरक्षा मिलती है। इसके लिए नीचे एक समाधान दिया गया है:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

इस सवाल के इस बारे में कुछ अच्छे जवाब हैं।

मेरा सुझाव है, पीडीओ का उपयोग करना सबसे अच्छा विकल्प है।

संपादित करें:

mysql_real_escape_string()PHP 5.5.0 के रूप में पदावनत किया गया है। Mysqli या PDO का उपयोग करें।

Mysql_real_escape_string () का एक विकल्प है

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

उदाहरण:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

169

एक सरल तरीका यह होगा कि आप PHPI फ्रेमवर्क का उपयोग करें जैसे CodeIgniter या Laravel जिसमें फ़िल्टरिंग और सक्रिय-रिकॉर्ड जैसी इनबिल्ट विशेषताएं हों ताकि आपको इन बारीकियों के बारे में चिंता न करनी पड़े।


7
मुझे लगता है कि इस तरह के ढांचे का उपयोग किए बिना प्रश्न को पूरा करना है।
शके

147

चेतावनी: इस उत्तर में वर्णित दृष्टिकोण केवल बहुत विशिष्ट परिदृश्यों पर लागू होता है और सुरक्षित नहीं है क्योंकि SQL इंजेक्शन के हमले केवल इंजेक्शन लगाने में सक्षम होने पर भरोसा नहीं करते हैं X=Y

यदि हमलावर PHP के $_GETचर के माध्यम से या URL के क्वेरी स्ट्रिंग के साथ फ़ॉर्म में हैक करने की कोशिश कर रहे हैं , तो आप उन्हें सुरक्षित नहीं होने पर उन्हें पकड़ने में सक्षम होंगे।

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

क्योंकि 1=1, 2=2, 1=2, 2=1, 1+1=2, आदि ... एक हमलावर की एक SQL डेटाबेस के लिए आम सवाल कर रहे हैं। शायद यह भी कई हैकिंग अनुप्रयोगों द्वारा उपयोग किया जाता है।

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


क्या हो रहा है 1-1=0? :)
रैल्पी आन्द्रा

@ RápliAndrás किसी प्रकार का ([0-9\-]+)=([0-9]+)
5ervant

127

PHP और MySQL के लिए बहुत सारे उत्तर हैं , लेकिन यहाँ SQL इंजेक्शन को रोकने के लिए PHP और Oracle के लिए कोड है और साथ ही oci8 ड्राइवरों के नियमित उपयोग के लिए:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

कृपया oci_bind_by_name मापदंडों की व्याख्या करें।
जहाँजैब एवान

127

एक अच्छा विचार Idiorm जैसे ऑब्जेक्ट-रिलेशनल मैपर का उपयोग करना है :

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

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


124

पदावनत की गई चेतावनी: इस उत्तर का नमूना कोड (जैसे प्रश्न का नमूना कोड) PHP के MySQLविस्तार का उपयोग करता है , जिसे PHP 5.5.0 में पदावनत किया गया था और पूरी तरह से PHP 7.0.0 में हटा दिया गया था।

सुरक्षा चेतावनी : यह उत्तर सुरक्षा सर्वोत्तम प्रथाओं के अनुरूप नहीं है। एसक्यूएल इंजेक्शन को रोकने के लिए बचने अपर्याप्त है , इसके बजाय तैयार किए गए बयानों का उपयोग करें। अपने जोखिम पर नीचे उल्लिखित रणनीति का उपयोग करें। (इसके अलावा, mysql_real_escape_string()PHP 7 में हटा दिया गया था)

SQL इंजेक्शन को रोकने के लिए PDO और MYSQLi का उपयोग करना एक अच्छा अभ्यास है, लेकिन यदि आप वास्तव में MySQL के कार्यों और प्रश्नों के साथ काम करना चाहते हैं, तो उपयोग करना बेहतर होगा

mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

इसे रोकने के लिए और अधिक क्षमताएं हैं: जैसे पहचान - यदि इनपुट एक स्ट्रिंग, संख्या, चार या सरणी है, तो इसका पता लगाने के लिए बहुत सारे इनबिल्ट फ़ंक्शन हैं। साथ ही, इनपुट डेटा की जांच के लिए इन कार्यों का उपयोग करना बेहतर होगा।

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

और इनपुट डेटा की जांच करने के लिए उन कार्यों का उपयोग करना बहुत बेहतर है mysql_real_escape_string


10
इसके अलावा, is_string () के साथ $ _POST सरणी सदस्यों की जाँच करने में बिल्कुल कोई मतलब नहीं है
आपका सामान्य ज्ञान

21
चेतावनी! mysql_real_escape_string() अचूक नहीं है
अर्ग्यल

10
mysql_real_escape_stringअब पदावनत कर दिया गया है, इसलिए इसका अब व्यवहार्य विकल्प नहीं है। इसे भविष्य में PHP से हटा दिया जाएगा। PHP या MySQL लोग क्या सलाह देते हैं, इस पर आगे बढ़ना।
jww

2
थीम: उपयोगकर्ता के सबमिट किए गए डेटा पर भरोसा न करें। आपके द्वारा अपेक्षित कुछ भी विशेष पात्रों या बूलियन तर्क के साथ एक कचरा डेटा है, जो आपको SQL क्वेरी का एक हिस्सा बनना चाहिए जिसे आप निष्पादित कर रहे हैं। $ _POST मूल्यों को केवल डेटा के रूप में रखें, न कि SQL भाग के लिए।
बिमल पौडेल

88

मैं इस छोटे से समारोह में कई साल पहले लिखा है:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

यह एक लाइनर C # -ish स्ट्रिंग में स्टेटमेंट चलाने की अनुमति देता है। जैसे:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

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

सुरक्षा अद्यतन: पिछले str_replaceसंस्करण उपयोगकर्ता डेटा में {#} टोकन जोड़कर इंजेक्शन की अनुमति दी। इस preg_replace_callbackसंस्करण में समस्याएँ नहीं होती हैं यदि प्रतिस्थापन में ये टोकन शामिल हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.