ID 배열이 주어지면 $galleries = array(1,2,5)WHERE 절에서 배열 값을 사용하는 SQL 쿼리를 원합니다.
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
MySQL과 함께 사용할이 쿼리 문자열을 어떻게 생성 할 수 있습니까?
ID 배열이 주어지면 $galleries = array(1,2,5)WHERE 절에서 배열 값을 사용하는 SQL 쿼리를 원합니다.
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
MySQL과 함께 사용할이 쿼리 문자열을 어떻게 생성 할 수 있습니까?
답변:
조심해! 이 답변에는 심각한 SQL 주입 취약점 이 포함되어 있습니다 . 외부 입력이 살균되지 않도록 여기에 제시된 코드 샘플을 사용하지 마십시오.
$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
$galleries이 문장 전에 입력을 확인해야한다는 경고를 추가하기 만하면됩니다 ! 준비된 명령문은 배열 AFAIK를 처리 할 수 없으므로 변수를 바인딩하는 데 익숙한 경우 여기에서 SQL 삽입을 쉽게 수행 할 수 있습니다.
$galleries값이 다음과 같다고 상상해보십시오 array('1); SELECT password FROM users;/*'). 위생 처리하지 않으면 쿼리는 SELECT * FROM galleries WHERE id IN (1); SELECT password FROM users;/*)입니다. 테이블 및 열 이름을 데이터베이스에있는 이름으로 변경하고 해당 쿼리를 시도하고 결과를 확인하십시오. 갤러리 목록이 아니라 결과로 비밀번호 목록을 찾을 수 있습니다. 데이터 출력 방법 또는 예기치 않은 데이터 배열로 스크립트가 수행하는 작업에 따라 공개보기로 출력 될 수 있습니다.
$galleries질문에 제공된 대로이 특정 코드를 설정하고 앞에서 언급 한 "sql injection 취약점"을 사용하여 악용 하는 거래는 어떻 습니까? 당신이 할 수없는 경우-당신은 미화 200 달러를 지불합니다. 어떻게에 대한?
PDO 사용 : [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 사용하기 [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 인젝션 공격을 방지하려면 먼저 ?각 입력 항목에 대해를 생성하여 매개 변수화 된 쿼리를 작성하십시오. 여기서는 ID를 포함하는 배열이 호출되었다고 가정합니다 $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()문자열과 함께 연산자 사용바운드 매개 변수로 인해 문자열과 정수를 쉽게 변경할 수 있습니다. PDO의 경우 변경이 필요하지 않습니다. MySQLi 변화 str_repeat('i',에str_repeat('s', 당신이 문자열을 확인해야합니다.
[1] : 간결성을 확인하는 오류를 생략했습니다. 각 데이터베이스 방법에 대해 일반적인 오류를 확인해야합니다 (또는 DB 드라이버가 예외를 발생하도록 설정).
[2] : PHP 5.6 이상이 필요합니다. 다시 한 번 간결성을 검사하는 오류를 생략했습니다.
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);다음이 ...ID를 확대하고 여러 매개 변수로 배열에서입니다. 당신이 참조 expr IN (value,...)한다면 그것은 더 많은 값이있을 수 있음을 의미합니다 WHERE id IN (1, 3, 4). 최소한 하나만 있으면됩니다.
...: wiki.php.net/rfc/argument_unpacking
사용하다:
select id from galleries where id in (1, 2, 5);
간단한 for each루프가 작동합니다.
Flavius / AvatarKava의 방법 이 더 좋지만 쉼표를 포함하는 배열 값이 없는지 확인하십시오.
로 플라 비우스 스테프의 대답 , 당신은 사용할 수 intval()있는지 모든 있도록 idINT 값은 다음과 같습니다
$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
대한 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)");
준비된 진술이있는 PDO의 경우 :
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
우리는 SQL 인젝션 취약점과 빈 상태를 처리해야 합니다. 다음과 같이 두 가지를 다룰 것입니다.
순수한 숫자 배열은 해당 타입 변환 즉 사용 intval하거나 floatval또는 doubleval각 소자 위에있다. mysqli_real_escape_string()원하는 경우 숫자 값에도 적용 할 수있는 문자열 유형 입니다. MySQL은 숫자뿐만 아니라 날짜 변형도 string으로 허용합니다 .
쿼리에 전달하기 전에 값을 적절히 이스케이프하려면 다음과 유사한 함수를 작성하십시오.
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);
Col. Shrapnel의 PHP 용 SafeMySQL 라이브러리는 매개 변수화 된 쿼리에 형식 힌트 자리 표시자를 제공하며 배열 작업을위한 편리한 자리 표시 자 몇 개를 포함합니다. 그만큼?a자리 이스케이프 문자열 * 쉼표로 구분 된 목록 배열을 확장한다.
예를 들면 다음과 같습니다.
$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
* MySQL은 자동 유형 강제 변환을 수행하므로 SafeMySQL이 위의 ID를 문자열로 변환해도 상관 없습니다. 여전히 올바른 결과를 얻을 수 있습니다.
입력 배열을 올바르게 필터링하면이 "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})";
당신은 테이블 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 나 다른 프로그래밍 언어를 사용할 필요없이 SQL 만 사용할 수 있습니다.
더 많은 예 :
$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();
PDO가없는 안전한 방법 :
$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 0 값 제거implode 모든 값을 IN 선택에 결합원래 질문은 숫자 배열과 관련이 있으며 문자열 배열을 사용하고 있으므로 주어진 예제를 작동시킬 수 없었습니다.
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) . "'";
'문자열 안에서? SQL 주입이 취약합니다. PDO :: quote 또는 mysqli_real_escape_string을 사용하십시오.
다음은 다른 데이터에 대해 명명 된 자리 표시 자와 함께 PDO를 사용하여 사용한 방법입니다. 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);
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 var는 다음과 같습니다.
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 ()
PDO :: quote ()