एक सरणी के लिए एक सरणी को पास करते हुए एक क्वेरी को पास करना


314

आईडी की एक सरणी को देखते हुए $galleries = array(1,2,5)मैं एक SQL क्वेरी है जो अपने WHERE क्लॉज में सरणी के मूल्यों का उपयोग करना चाहता हूं जैसे:

SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */

मैं MySQL के साथ उपयोग करने के लिए यह क्वेरी स्ट्रिंग कैसे उत्पन्न कर सकता हूं?



5
@ यह एक सबसे पुराना (2009) है।
फबीआन

क्या समस्या का कोई समान समाधान है जैसे कि * FABLE TABLE WHERE NAME LIKE ('ABC%' या 'ABD%' OR ..) से
यूजीन जोसेफ

जवाबों:


332

सावधान! इस उत्तर में एक गंभीर SQL इंजेक्शन है भेद्यता है। यहां प्रस्तुत किए गए कोड के नमूनों का उपयोग न करें, यह सुनिश्चित किए बिना कि कोई भी बाहरी इनपुट सैनिटाइज है।

$ids = join("','",$galleries);   
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";

7
पहचानकर्ता अभी भी एक उद्धृत सूची है, इसलिए यह उदाहरण के लिए "WHERE आईडी IN ('1,2,3,4') के रूप में सामने आता है। आपको प्रत्येक पहचानकर्ता को अलग से उद्धृत करने की आवश्यकता है, अन्यथा कोष्ठकों के अंदर उद्धरणों को खोदें।
रोब

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

3
अपने जैसे PHP के लिए नए शौक के लिए, क्या कोई समझा सकता है, या मुझे समझाने के लिए एक संसाधन की ओर इशारा कर सकता है, यह इंजेक्शन लगाने का खतरा क्यों है और इसे रोकने के लिए यह कैसे सही तरीके से किया जाना चाहिए? क्या होगा अगर इस अगली क्वेरी को चलाने से तुरंत पहले आईडी की सूची एक क्वेरी से उत्पन्न होती है, क्या यह अभी भी खतरनाक है?
ministe2003 13

3
@ ministe2003 कल्पना करें $galleriesकि निम्नलिखित मूल्य था array('1); SELECT password FROM users;/*'):। यदि आप इसकी पुष्टि नहीं करते हैं, तो क्वेरी पढ़ा जाएगा SELECT * FROM galleries WHERE id IN (1); SELECT password FROM users;/*)। अपने डेटाबेस में मौजूद किसी चीज़ के लिए तालिका और स्तंभ नाम बदलें और उस क्वेरी का प्रयास करें, परिणाम देखें। आप दीर्घाओं की सूची के बजाय अपने परिणाम के रूप में पासवर्ड की एक सूची प्राप्त करेंगे। इस बात पर निर्भर करता है कि डेटा कैसे आउटपुट है, या स्क्रिप्ट अप्रत्याशित डेटा की एक सरणी के साथ क्या करती है, जो कि सार्वजनिक दृश्य में आउटपुट प्राप्त कर सकती है ... ouch।
क्रिस बेकर

18
के लिए पूछा सवाल यह एक पूरी तरह से वैध और एक सुरक्षित जवाब है। जो कोई भी यह शिकायत करता है कि यह सुरक्षित नहीं है - उस सौदे के बारे में जो मैंने इस विशेष कोड $galleriesको प्रश्न में दिए गए तरीके से सेट किया है और आप उपरोक्त "sql इंजेक्शन भेद्यता" का उपयोग करके इसका फायदा उठाते हैं। यदि आप नहीं कर सकते - आप मुझे USD200 का भुगतान करें। उस के बारे में कैसा है?
zerkms

307

पीडीओ का उपयोग करना: [१]

$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);

MySQLi [2] का उपयोग करना

$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();

स्पष्टीकरण:

IN()किसी दिए गए सूची में मान मौजूद है या नहीं यह जाँचने के लिए SQL ऑपरेटर का उपयोग करें ।

सामान्य तौर पर यह इस तरह दिखता है:

expr IN (value,...)

हम ()अपने एरे से अंदर जाने के लिए एक अभिव्यक्ति का निर्माण कर सकते हैं । ध्यान दें कि कोष्ठक के अंदर कम से कम एक मान होना चाहिए या MySQL एक त्रुटि लौटाएगा; यह सुनिश्चित करने के लिए समान है कि हमारे इनपुट सरणी का कम से कम एक मूल्य है। SQL इंजेक्शन हमलों के खिलाफ रोकने में मदद करने के लिए, पहले ?एक पैरामीटर क्वेरी बनाने के लिए प्रत्येक इनपुट आइटम के लिए उत्पन्न करें। यहाँ मैं मानता हूँ कि आपके आईडी वाले सरणी को कहा जाता है $ids:

$in = join(',', array_fill(0, count($ids), '?'));

$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;

तीन वस्तुओं के इनपुट सरणी को देखते हुए ऐसा $selectलगेगा:

SELECT *
FROM galleries
WHERE id IN (?, ?, ?)

फिर से ध्यान दें कि ?इनपुट ऐरे में प्रत्येक आइटम के लिए है। फिर हम क्वेरी को तैयार करने और निष्पादित करने के लिए PDO या MySQLi का उपयोग करेंगे।

IN()तार के साथ ऑपरेटर का उपयोग करना

बाध्य मापदंडों के कारण स्ट्रिंग और पूर्णांक के बीच बदलना आसान है। पीडीओ के लिए किसी बदलाव की आवश्यकता नहीं है; MySQLi परिवर्तन के str_repeat('i',लिएstr_repeat('s',यदि आपको स्ट्रिंग्स की जांच करने की आवश्यकता है तो ।

[१]: मैंने संक्षिप्तता के लिए कुछ त्रुटि जाँच को छोड़ दिया है। आपको प्रत्येक डेटाबेस विधि के लिए सामान्य त्रुटियों की जांच करने की आवश्यकता है (या अपवादों को फेंकने के लिए अपने DB ड्राइवर को सेट करें)।

[२]: PHP ५.६ या उच्चतर की आवश्यकता है। फिर से मैंने संक्षिप्तता के लिए कुछ त्रुटि जाँच को छोड़ दिया है।


7
क्या करता है ... $ ids करते हैं? मुझे "सिंटैक्स त्रुटि, अप्रत्याशित '।'
मार्सेल

मैं उन्हें देखता हूं, मैं MySQLi का उपयोग कर रहा हूं और मेरे पास php 5.6
Marcel

1
यदि आप जिक्र कर रहे हैं, $statement->bind_param(str_repeat('i', count($ids)), ...$ids);तो ...एक सरणी से आईडी को कई मापदंडों में विस्तारित कर रहा है। अगर आप expr IN (value,...)इसका जिक्र कर रहे हैं तो इसका मतलब है कि इसके और भी उदाहरण हो सकते हैं WHERE id IN (1, 3, 4)। बस कम से कम एक होने की जरूरत है।
लेवी मॉरिसन

1
मैं उलझन में था कि <<< क्या था, लेकिन मुझे एक संदर्भ मिला: php.net/manual/en/…
Tsangares

1
इसके अलावा, यहाँ के लिए संदर्भ है ...: wiki.php.net/rfc/argument_unpacking
Tsangares

58

ints:

$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";

तार:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";

1
Why '\' ?? कृपया मुझे बताएं
zloctb

29

आप अपने इनपुट्स को पहले ही ठीक से मान लें ...

$matches = implode(',', $galleries);

फिर अपनी क्वेरी समायोजित करें:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

आपके डेटासेट के आधार पर उचित रूप से उद्धरण मूल्य।


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



7

पलायन कार्य के साथ MySQLi के लिए :

$ids = array_map(function($a) use($mysqli) { 
    return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a;
  }, $ids);
$ids = join(',', $ids);  
$result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");

तैयार विवरण के साथ पीडीओ के लिए:

$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);

यह अच्छा है, छोटा है और कोड प्रविष्टि भेद्यता से बचा जाता है! +1
स्टीफन रिक्टर

MySQLi ने स्टेटमेंट भी तैयार किए हैं। अपने इनपुट से बच न जाएं, यह संभवतः SQL इंजेक्शन के लिए कमजोर है।
धर्मन

6

हमें SQL इंजेक्शन भेद्यताओं और एक खाली स्थिति का ध्यान रखना चाहिए । मैं नीचे के रूप में दोनों को संभालने जा रहा हूं।

एक शुद्ध संख्यात्मक सरणी के लिए, प्रत्येक तत्व पर intvalया floatvalउसके doublevalऊपर उचित प्रकार के रूपांतरण का उपयोग करें । mysqli_real_escape_string()यदि आप चाहें तो स्ट्रिंग प्रकारों के लिए भी संख्यात्मक मूल्यों पर लागू किया जा सकता है। MySQL नंबरों के साथ-साथ डेट वेरिएंट को भी स्ट्रिंग की अनुमति देता है

क्वेरी से गुजरने से पहले मानों को उचित रूप से बाहर निकलने के लिए, इसके समान फ़ंक्शन बनाएं:

function escape($string)
{
    // Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
    return mysqli_real_escape_string($db, $string);
}

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

स्ट्रिंग सरणी को संचित करें जैसे:

$values = array_map('escape', $gallaries);

एक अंकीय सरणी का उपयोग कर स्वच्छ किया जा सकता है intvalया floatvalया doublevalबजाय उपयुक्त के रूप में:

$values = array_map('intval', $gallaries);

फिर अंत में क्वेरी कंडीशन बनाएँ

$where  = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;

या

$where  = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;

चूंकि सरणी कभी-कभी खाली भी हो सकती है, $galleries = array();इसलिए हमें ध्यान देना चाहिए कि IN ()खाली सूची की अनुमति नहीं है। ORइसके बजाय एक भी उपयोग कर सकते हैं , लेकिन समस्या बनी हुई है। तो उपरोक्त जाँच, count($values)सुनिश्चित करना है।

और इसे अंतिम क्वेरी में जोड़ें:

$query  = 'SELECT * FROM `galleries` WHERE ' . $where;

टीआईपी : यदि आप सभी पंक्तियों को छिपाने के बजाय खाली सरणी के मामले में सभी रिकॉर्ड (कोई फ़िल्टरिंग) नहीं दिखाना चाहते हैं, तो बस टर्नरी के झूठे हिस्से में 0 को 1 के साथ बदलें ।


मेरे समाधान को एक-लाइनर (और बदसूरत एक) बनाने के लिए , बस किसी के मामले में:$query = 'SELECT * FROM galleries WHERE ' . (count($gallaries) ? "id IN ('" . implode("', '", array_map('escape', $gallaries)) . "')" : 0);
इज़हार आज़मी

5

सुरक्षित।

$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";

5

PHP के लिए Col. Shrapnel की SafeMySQL लाइब्रेरी अपने पैरामीट्राइज़्ड प्रश्नों में टाइप- हिंटेड प्लेसहोल्डर्स प्रदान करती है, और सरणियों के साथ काम करने के लिए सुविधाजनक प्लेसहोल्डर्स के एक जोड़े को शामिल करती है। ?aप्लेसहोल्डर भाग निकले तार * की अल्पविराम से अलग की सूची में एक सरणी बाहर फैलता है।

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

$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);

* ध्यान दें कि चूंकि MySQL स्वचालित प्रकार का जबरदस्ती करता है, इसलिए इससे कोई फर्क नहीं पड़ता कि SafeMySQL उपर्युक्त आईडी को स्ट्रिंग में बदल देगा - फिर भी आपको सही परिणाम मिलेगा।


4

यदि हम इनपुट ऐरे को ठीक से फ़िल्टर करते हैं तो हम इस "WHERE id IN" क्लॉज़ का उपयोग कर सकते हैं। कुछ इस तरह:

$galleries = array();

foreach ($_REQUEST['gallery_id'] as $key => $val) {
    $galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}

नीचे दिए गए उदाहरण की तरह:यहां छवि विवरण दर्ज करें

$galleryIds = implode(',', $galleries);

यानी अब आपको सुरक्षित रूप से उपयोग करना चाहिए $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";


@ लेवी-मॉरिसन ने इसका बहुत बेहतर समाधान पोस्ट किया।
सुप्रतीम रॉय

4

आपके पास टेबल texts (T_ID (int), T_TEXT (text))और टेबल हो सकती हैtest (id (int), var (varchar(255)))

में insert into test values (1, '1,2,3') ;तालिका ग्रंथों से निम्नलिखित इच्छा उत्पादन पंक्तियों जहां T_ID IN (1,2,3):

SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0

इस तरह से आप एक अतिरिक्त तालिका के बिना एक सरल n2m डेटाबेस संबंध और PHP या कुछ अन्य प्रोग्रामिंग भाषा का उपयोग करने की आवश्यकता के बिना केवल एसक्यूएल का उपयोग कर प्रबंधन कर सकते हैं।


3

एक और उदाहरण:

$galleryIds = [1, '2', 'Vitruvian Man'];
$ids = array_filter($galleryIds, function($n){return (is_numeric($n));});
$ids = implode(', ', $ids);

$sql = "SELECT * FROM galleries WHERE id IN ({$ids})";
// output: 'SELECT * FROM galleries WHERE id IN (1, 2)'

$statement = $pdo->prepare($sql);
$statement->execute();

2

IN क्वेरी का उपयोग करने के अलावा, आपके पास ऐसा करने के लिए दो विकल्प होते हैं जैसे कि एक IN क्वेरी में SQL इंजेक्शन भेद्यता का जोखिम होता है। आप इच्छित डेटा प्राप्त करने के लिए लूपिंग का उपयोग कर सकते हैं या आप OR केस के साथ क्वेरी का उपयोग कर सकते हैं

1. SELECT *
      FROM galleries WHERE id=1 or id=2 or id=5;


2. $ids = array(1, 2, 5);
   foreach ($ids as $id) {
      $data[] = SELECT *
                    FROM galleries WHERE id= $id;
   }

2

पीडीओ के बिना सुरक्षित तरीका:

$ids = array_filter(array_unique(array_map('intval', (array)$ids)));

if ($ids) {
    $query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
  • (array)$idsकास्ट $idsसरणी चर
  • array_map सभी सरणी मानों को पूर्णांकों में परिवर्तित करें
  • array_unique बार-बार मान निकालें
  • array_filter शून्य मान निकालें
  • implode IN चयन में सभी मानों को मिलाएँ

1

क्योंकि मूल प्रश्न संख्याओं की एक सरणी से संबंधित है और मैं स्ट्रिंग के एक सरणी का उपयोग कर रहा हूं जो दिए गए उदाहरणों को काम नहीं कर सका।

मैंने पाया कि प्रत्येक स्ट्रिंग को IN()फंक्शन के साथ काम करने के लिए सिंगल कोट्स में एनकैप्सुलेट करने की आवश्यकता होती है ।

यहाँ मेरा समाधान है

foreach($status as $status_a) {
        $status_sql[] = '\''.$status_a.'\'';
    }
    $status = implode(',',$status_sql);

$sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");

जैसा कि आप देख सकते हैं पहला फ़ंक्शन प्रत्येक सरणी चर को लपेटता है single quotes (\')और फिर सरणी को निहित करता है।

नोट: $statusSQL कथन में एकल उद्धरण नहीं है।

संभवतः उद्धरण जोड़ने का एक अच्छा तरीका है, लेकिन यह काम करता है।


या$filter = "'" . implode("','",$status) . "'";
एलेजांद्रो सलामांका मजुएलो

1
यह इंजेक्शन-कमजोर है।
मार्क अमेरी

तार से बचकर कहाँ जाना है? उदाहरण के लिए 'स्ट्रिंग के अंदर? एसक्यूएल इंजेक्शन असुरक्षित। PDO :: उद्धरण या mysqli_real_escape_string का उपयोग करें।
18C

1

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

$owner_id = 123;
$galleries = array(1,2,5,'abc');

$good_galleries = array_filter($chapter_arr, 'is_numeric');

$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
    "OWNER_ID" => $owner_id,
));

$data = $stmt->fetchAll(PDO::FETCH_ASSOC);

-3

SQL इंजेक्शन को रोकने के लिए मूल तरीके हैं:

  • तैयार किए गए कथनों और मानकीकृत प्रश्नों का उपयोग करें
  • अपने असुरक्षित चर में विशेष वर्णों को बचाना

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

आप array_mapतत्वों में से प्रत्येक में एक उद्धरण जोड़ने का उपयोग करके प्रश्नों को उत्पन्न कर सकते हैं $galleries:

$galleries = array(1,2,5);

$galleries_str = implode(', ',
                     array_map(function(&$item){
                                   return "'" .mysql_real_escape_string($item) . "'";
                               }, $galleries));

$sql = "SELECT * FROM gallery WHERE id IN (" . $galleries_str . ");";

उत्पन्न $ sql संस्करण होगा:

SELECT * FROM gallery WHERE id IN ('1', '2', '5');

नोट: mysql_real_escape_string , जैसा कि यहां इसके प्रलेखन में वर्णित है , PHP 5.5.0 में पदावनत किया गया था, और इसे PHP 7.0.0 में हटा दिया गया था। इसके बजाय, MySQLi या PDO_MySQL एक्सटेंशन का उपयोग किया जाना चाहिए। MySQL भी देखें: अधिक जानकारी के लिए API गाइड और संबंधित FAQ चुनना। इस समारोह के विकल्प में शामिल हैं:

  • mysqli_real_escape_string ()

  • पीडीओ :: उद्धरण ()


4
इतना ही नहीं, यह अन्य उत्तरों की तुलना में कुछ भी नया नहीं करता है, यह इंजेक्शन-कमजोर है, स्वीकार किए जाते हैं जवाब के बावजूद SQL इंजेक्शन के बारे में चेतावनी के वर्षों के लिए पोस्ट किया गया था।
मार्क अमेरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.