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()
있는지 모든 있도록 id
INT 값은 다음과 같습니다
$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 (\')
을 함축합니다.
참고 : $status
SQL 문에는 작은 따옴표가 없습니다.
따옴표를 추가하는 더 좋은 방법이 있지만 이것이 효과가 있습니다.
$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 ()