WHERE deyimi kullanarak bir sorguyu dizi geçirme


314

Kimlikleri bir dizi verilen $galleries = array(1,2,5)ben onun WHERE yan tümcesinde dizinin değerlerini kullanan bir SQL sorgusu istiyorum:

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

MySQL ile kullanmak için bu sorgu dizesini nasıl oluşturabilirim?



5
@trante bu en eski olanı (2009).
Fabián

SEÇİM ADI NEREDE
SEÇİLDİĞİ TABLO'DAN

Yanıtlar:


332

DİKKAT! Bu yanıt ciddi bir SQL enjeksiyon güvenlik açığı içeriyor . Herhangi bir harici girişin sterilize edilmediğinden emin olmadan kod örneklerini burada gösterildiği gibi KULLANMAYIN.

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

7
Tanımlayıcılar hala tırnak içine alınmış bir liste olduğundan, örneğin "NEREDE ID IN ('1,2,3,4')" olarak görünür. Her tanımlayıcıyı ayrı olarak alıntılamanız veya tırnak işaretlerini parantez içine almanız gerekir.
Rob

22
Ben sadece $galleriesbu ifadeden önce giriş doğrulanması gereken uyarı ekleyin ! Hazırlanan ifadeler AFAIK dizilerini işleyemez, bu nedenle ilişkili değişkenlere alışkınsanız, burada SQL enjeksiyonunu kolayca yapabilirsiniz.
leemes

3
benim gibi PHP yeni başlayanlar için, biri açıklayabilir, ya da bana açıklamak için bir kaynağa işaret edebilir, bu neden enjeksiyona eğilimli ve bunu önlemek için nasıl doğru yapılması gerekir? Bu bir sonraki sorgu çalıştırılmadan hemen önce bir sorgudan kimlik listesi oluşturulursa, bu hala tehlikeli midir?
ministe2003

3
eğer @ Hayal ministe2003 $galleriesaşağıdaki değeri vardı: array('1); SELECT password FROM users;/*'). Bunu sterilize etmezseniz, sorgu okunacaktır SELECT * FROM galleries WHERE id IN (1); SELECT password FROM users;/*). Tablo ve sütun adlarını veritabanınızda bulunan bir şeyle değiştirin ve bu sorguyu deneyin, sonuçları kontrol edin. Galerilerin bir listesi yerine, sonuç olarak bir şifre listesi bulacaksınız. Verilerin nasıl çıktısına veya komut dosyasının beklenmedik bir veri dizisiyle ne yaptığına bağlı olarak, genel görünüme çıktı alınabilir ... ouch.
Chris Baker

18
İçin sorulan soruya o mükemmel geçerli ve güvenli bir cevabı var. Kim şikayet ederse güvenli değil - bu özel kodu $galleriessoruda belirtildiği gibi ayarladığım ve yukarıda belirtilen "sql enjeksiyon güvenlik açığı" kullanarak sömürüyorum. Yapamazsan bana 200 USD ödersin. Peki ya bu?
zerkms

307

PDO kullanma: [1]

$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 Kullanımı [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();

Açıklama:

IN()Belirli bir listede bir değerin olup olmadığını kontrol etmek için SQL operatörünü kullanın .

Genel olarak şöyle görünür:

expr IN (value,...)

Dizimizin içine yerleştirmek için bir ifade oluşturabiliriz (). Parantez içinde en az bir değer olması gerektiğine dikkat edin, yoksa MySQL bir hata döndürür; bu, girdi dizimizin en az bir değerinin olduğundan emin olmak anlamına gelir. SQL enjeksiyon saldırılarına karşı önlemeye yardımcı olmak için, önce ?her girdi öğesi için parametreli bir sorgu oluşturmak üzere bir tane oluşturun. Burada kimlikleri içeren dizinin çağrıldığını varsayıyorum $ids:

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

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

Verilen üç öğeden oluşan bir giriş dizisi $selectşöyle görünecektir:

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

Yine ?, girdi dizisindeki her öğe için bir tane olduğunu unutmayın . Ardından, sorguyu yukarıda belirtildiği gibi hazırlamak ve yürütmek için PDO veya MySQLi kullanacağız.

IN()İşleci dizelerle kullanma

Bağlı parametreler nedeniyle dizeler ve tamsayılar arasında geçiş yapmak kolaydır. PDO için herhangi bir değişiklik gerekmez; MySQLi değişim için str_repeat('i',için str_repeat('s',size dizeleri kontrol etmek gerekiyorsa.

[1]: Kısalık için bazı hata denetimlerini atladım. Her veritabanı yöntemi için olağan hataları kontrol etmeniz (veya DB sürücünüzü istisnalar atacak şekilde ayarlamanız) gerekir.

[2]: PHP 5.6 veya üstü sürüm gerektirir. Yine kısalık için bazı hata denetimini atladım.


7
... $ ids ne yapıyor? "Sözdizimi hatası, beklenmeyen '."
Marcel

Onları görüyorum, MySQLi kullanıyorum ve php 5.6
Marcel

1
Eğer atıfta varsa $statement->bind_param(str_repeat('i', count($ids)), ...$ids);o zaman ...kimliği genişletiyor birden parametrelere bir diziden bu. Eğer atıfta bulunuyorsanız, expr IN (value,...)bu sadece daha fazla değer olabileceği anlamına gelir WHERE id IN (1, 3, 4). Sadece en az bir tane olmalı.
Levi Morrison

1
Ben <<< ne olduğunu karıştı ama bir referans buldum: php.net/manual/en/…
Tsangares

1
Ayrıca, işte referans ...: wiki.php.net/rfc/argument_unpacking
Tsangares

58

int:

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

Teller:

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

1
Neden '\' ?? Lütfen söyle
zloctb

29

Girişlerinizi önceden uygun bir şekilde sterilize ettiğinizi varsayarak ...

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

Ardından sorgunuzu ayarlayın:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

Veri kümenize bağlı olarak değerleri uygun şekilde belirtin.


Ben ne teklif denedim ama sadece ilk anahtar değeri getirildi. Bunun mantıklı olmadığını biliyorum, ancak user542568 örneğini kullanarak yaparsam, lanet olası şey işe yarıyor.
Samuel Ramzan


9

As Flavius Stef cevabı kullanabileceğiniz intval()emin tüm yapmak için idint değerler şunlardır:

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

7

İçin MySQLi bir kaçış fonksiyonu ile:

$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)");

Hazırlanan ifadeye sahip PDO için:

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

Bu güzel, kısa ve kod ekleme güvenlik açığı önler! +1
Stephan Richter

MySQLi de ifadeler hazırladı. Girişinizden kaçmayın, bu potansiyel olarak SQL enjeksiyonuna karşı savunmasızdır.
Dharman

6

SQL enjeksiyon güvenlik açıklarına ve boş bir duruma dikkat etmeliyiz . Her ikisini de aşağıdaki gibi ele alacağım.

Saf sayısal dizi için, uygun tür dönüştürme viz kullanmak intvalya da floatvalya da doublevalher bir elemanın üzerinde. mysqli_real_escape_string()İsterseniz sayısal değerlere de uygulanabilecek dize türleri için. MySQL, sayıların yanı sıra tarih değişkenlerini dize olarak sağlar .

Sorguya geçmeden önce değerlerden uygun şekilde kaçmak için aşağıdakine benzer bir işlev oluşturun:

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

Böyle bir işlev büyük olasılıkla uygulamanızda zaten kullanılabilir olacaktır veya belki de zaten bir işlev oluşturmuş olabilirsiniz.

Dize dizisini aşağıdaki gibi sterilize edin:

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

Sayısal bir dizi kullanarak sterilize edilebilir intvalya da floatvalya da doublevalyerine uygun olarak:

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

Son olarak sorgu koşulunu oluşturun

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

veya

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

Dizi bazen boş $galleries = array();olabileceğinden IN (), boş bir listeye izin vermediğini belirtmeliyiz. Bunun ORyerine kişi de kullanılabilir , ancak sorun devam etmektedir. Yani yukarıdaki kontrol, count($values)aynı şeyi sağlamaktır.

Ve son sorguya ekleyin:

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

İPUCU : Tüm satırları gizlemek yerine boş bir dizi durumunda tüm kayıtları göstermek (filtreleme yok) istiyorsanız , üçgenin yanlış bölümünde 0 yerine 1 yazın.


Çözümümün bir kişinin (ve çirkin bir) olmasını sağlamak için , birinin ihtiyacı olması durumunda:$query = 'SELECT * FROM galleries WHERE ' . (count($gallaries) ? "id IN ('" . implode("', '", array_map('escape', $gallaries)) . "')" : 0);
Izhar Aazmi

5

Daha güvenli.

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

5

Col. Shrapnel'in PHP için SafeMySQL kütüphanesi, parametrik sorgularında tip ipucu yer tutucuları sağlar ve dizilerle çalışmak için birkaç uygun yer tutucu içerir. Yer ?atutucu diziyi virgülle ayrılmış bir çıkış karakter dizisi listesine genişletir *.

Örneğin:

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

* MySQL otomatik tip zorlaması gerçekleştirdiğinden, SafeMySQL'in yukarıdaki kimlikleri dizelere dönüştürmesi önemli değildir - yine de doğru sonucu elde edersiniz.


4

Giriş dizisini doğru şekilde filtrelersek bu "WHERE id IN" yan tümcesini kullanabiliriz. Bunun gibi bir şey:

$galleries = array();

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

Aşağıdaki örnek gibi:resim açıklamasını buraya girin

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

Yani şimdi güvenle kullanmalısın $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";


@ levi-morrison buna çok daha iyi bir çözüm gönderdi.
Supratim Roy

4

Masanız texts (T_ID (int), T_TEXT (text))ve masanız olabilirtest (id (int), var (varchar(255)))

Gelen insert into test values (1, '1,2,3') ;tablo metinlerden şu veriler çıktı sıralar nerede 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

Bu şekilde, ekstra bir tablo olmadan ve PHP veya başka bir programlama dili kullanmaya gerek kalmadan yalnızca SQL kullanarak basit bir n2m veritabanı ilişkisini yönetebilirsiniz.


3

Daha fazla örnek:

$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 sorgusunu kullanmanın yanı sıra, IN sorgusunda SQL enjeksiyon güvenlik açığı riski olduğu için iki seçeneğiniz vardır. İstediğiniz kesin verileri elde etmek için döngüyü kullanabilir veya sorguyu VEYA büyük / küçük harf ile kullanabilirsiniz

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

PDO olmadan güvenli yol:

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

if ($ids) {
    $query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
  • (array)$idsDökme $idsdizi değişken
  • array_map Tüm dizi değerlerini tamsayılara dönüştürme
  • array_unique Tekrarlanan değerleri kaldır
  • array_filter Sıfır değerlerini kaldır
  • implode Tüm değerleri IN seçimine katıl

1

Orijinal soru bir sayı dizisi ile ilgili ve ben bir dizi dizeleri kullanıyorum çünkü verilen örnekleri işe yaramadı.

Her dize IN()işlev ile çalışmak için tek tırnak içine alınması gerektiğini bulundu .

İşte benim çözümüm

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

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

Gördüğünüz gibi, ilk işlev her dizi değişkenini sarar single quotes (\')ve sonra diziyi yerleştirir.

Not: $statusSQL deyiminde tek tırnak yok.

Muhtemelen tırnak eklemenin daha güzel bir yolu var ama bu işe yarıyor.


Veya$filter = "'" . implode("','",$status) . "'";
Alejandro Salamanca Mazuelo

1
Bu, enjeksiyona açıktır.
Mark Amery

Dizelerden kaçmak nerede? Örneğin 'dize içinde? SQL Injection savunmasız. PDO :: quote veya mysqli_real_escape_string kullanın.
18C

1

Aşağıda, diğer veriler için adlandırılmış yer tutucularla PDO kullanarak kullandığım yöntem verilmiştir. SQL enjeksiyonunun üstesinden gelmek için sadece tamsayı olan değerleri kabul etmek ve diğerlerini reddetmek için diziyi filtreliyorum.

$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 enjeksiyonunu önlemek için temel yöntemler şunlardır:

  • Hazırlanan ifadeleri ve parametreli sorguları kullanma
  • Güvenli olmayan değişkeninizdeki özel karakterlerden kaçma

Hazırlanan ifadeleri ve parametreli sorgu sorgusu kullanmak daha iyi bir uygulama olarak kabul edilir, ancak kaçan karakterler yöntemini seçerseniz, aşağıdaki örneğimi deneyebilirsiniz.

Aşağıdakilerdeki array_mapöğelerin her birine tek bir tırnak eklemek için sorguları oluşturabilirsiniz $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 . ");";

Oluşturulan $ sql var:

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

Not: mysql_real_escape_string , buradaki belgelerinde açıklandığı gibi PHP 5.5.0'da kaldırılmıştır ve PHP 7.0.0'da kaldırılmıştır. Bunun yerine, MySQLi veya PDO_MySQL uzantısı kullanılmalıdır. Daha fazla bilgi için ayrıca bkz. MySQL: API kılavuzu seçme ve ilgili SSS. Bu işleve alternatifler şunları içerir:

  • mysqli_real_escape_string ()

  • PDO :: alıntı ()


4
Bu, diğer cevaplara kıyasla yeni bir şey eklemekle kalmaz, kabul edilen cevabın yıllarca altında SQL enjeksiyonu hakkında uyarıları olmasına rağmen, enjeksiyona karşı savunmasızdır.
Mark Amery
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.