2009-08-17 4 views
25

PHP로 mysql에서 prepared statement를 사용하는 것이 처음이다. 열을 검색하기 위해 준비된 문을 작성하는 데 도움이 필요합니다.PHP에서 안전한 mysql prepared statement를 생성하는 방법은 무엇입니까?

다른 열에서 정보를 얻어야합니다. 준비

$qry = "SELECT * FROM mytable where userid='{$_GET['userid']}' AND category='{$_GET['category']}'ORDER BY id DESC" 
$result = mysql_query($qry) or die(mysql_error()); 

누군가가 (위와 같이) 나 URL 매개 변수의 입력을 사용하여 보안 MySQL의 문을 만들 수 있습니다 : 현재 테스트 파일, 나는 완전히 안전하지 않은 SQL 문을 사용하여?

보너스 : 준비된 진술은 속도를 높이는 것으로 생각됩니다. 페이지에서 준비된 문장을 3 ~ 4 번만 사용하면 전반적인 속도가 향상됩니까?

+1

가능한 중복 [ PHP에서 SQL 주입을 막는 가장 좋은 방법] (http://stackoverflow.com/questions/60174/best-way-to-stop-sql-injection-in-php) – outis

답변

45

여기 mysqli에게 (객체 구문 - 당신이 원하는 경우 구문을 기능 번역 매우 쉽게)를 사용하는 예이다 :,

$db = new mysqli("host","user","pw","database"); 
$stmt = $db->prepare("SELECT * FROM mytable where userid=? AND category=? ORDER BY id DESC"); 
$stmt->bind_param('ii', intval($_GET['userid']), intval($_GET['category'])); 
$stmt->execute(); 

$stmt->store_result(); 
$stmt->bind_result($column1, $column2, $column3); 

while($stmt->fetch()) 
{ 
    echo "col1=$column1, col2=$column2, col3=$column3 \n"; 
} 

$stmt->close(); 

또한 당신은 SELECT와 함께 사용하기 위해 (연관 배열을 잡아하는 쉬운 방법을 원하는 경우

:

function stmt_bind_assoc (&$stmt, &$out) { 
    $data = mysqli_stmt_result_metadata($stmt); 
    $fields = array(); 
    $out = array(); 

    $fields[0] = $stmt; 
    $count = 1; 

    while($field = mysqli_fetch_field($data)) { 
     $fields[$count] = &$out[$field->name]; 
     $count++; 
    } 
    call_user_func_array(mysqli_stmt_bind_result, $fields); 
} 

그냥 대신 bind_result 호출하는 그것을 호출을 사용하려면 : *) 대신에 정확히 어떤 변수에 바인딩을 지정할 필요없이, 여기에 편리한 기능입니다

+0

. 이 성명서의 결과를 어떻게 표시합니까? 일반적으로 mysql_fetch_row를 사용합니다. 여기서 작동합니까? – chris

+0

결과를 얻는 방법에 대한 예제를 추가하여 업데이트했습니다. – Amber

+0

또한 필자의 예제에서는 두 매개 변수가 모두 정수라고 가정합니다. 문자열 인 경우 그에 따라 bind_param 인수를 변경해야합니다. – Amber

2

PHP로 MySQL과 관련된 보안 (또는 그 문제와 관련된 다른 언어)은 크게 논의 된 문제입니다. 내 의견

두 가지 가장 중요한 항목입니다 : 여기에 몇 가지 좋은 팁 데리러 몇 곳은

  • SQL 삽입 : PHP의 mysql_real_escape_string() 함수 (또는 이와 유사한 함수)를 사용하여 모든 쿼리 변수를 이스케이프해야합니다.
  • 입력 유효성 검사 : 사용자의 입력을 신뢰하지 마십시오. 입력 내용을 제대로 살균하고 검증하는 방법에 대한 자습서는 this을 참조하십시오.
+1

mysql_real_escape_string은 훌륭하지 않으며 문자열 컨텍스트 비 문자열 필드를 시도하고 보호하기 위해 절대 사용하지 마십시오. 제발,이 블랙리스트 문자열 필터링 반창고보다 우선적으로 바운드 매개 변수를 사용하십시오. – Cheekysoft

+1

@James : 입력 유효성 검사 링크가 매우 유용합니다. 감사! 덕분에 – chris

11

대신이를 작성할 수 있습니다

$qry = "SELECT * FROM mytable where userid='"; 
$qry.= mysql_real_escape_string($_GET['userid'])."' AND category='"; 
$qry.= mysql_real_escape_string($_GET['category'])."' ORDER BY id DESC"; 

그러나 사용 PDO

<?php 
/* Execute a prepared statement by passing an array of values */ 
$sth = $dbh->prepare('SELECT * FROM mytable where userid=? and category=? 
         order by id DESC'); 
$sth->execute(array($_GET['userid'],$_GET['category'])); 
//Consider a while and $sth->fetch() to fetch rows one by one 
$allRows = $sth->fetchAll(); 
?> 

또는처럼, 당신이 더 일반적인 라이브러리를 사용할 준비가 문을 사용하는 mysqli

<?php 
$link = mysqli_connect("localhost", "my_user", "my_password", "world"); 

/* check connection */ 
if (mysqli_connect_errno()) { 
    printf("Connect failed: %s\n", mysqli_connect_error()); 
    exit(); 
} 

$category = $_GET['category']; 
$userid = $_GET['userid']; 

/* create a prepared statement */ 
if ($stmt = mysqli_prepare($link, 'SELECT col1, col2 FROM mytable where 
         userid=? and category=? order by id DESC')) { 
    /* bind parameters for markers */ 
    /* Assumes userid is integer and category is string */ 
    mysqli_stmt_bind_param($stmt, "is", $userid, $category); 
    /* execute query */ 
    mysqli_stmt_execute($stmt); 
    /* bind result variables */ 
    mysqli_stmt_bind_result($stmt, $col1, $col2); 
    /* fetch value */ 
    mysqli_stmt_fetch($stmt); 
    /* Alternative, use a while: 
    while (mysqli_stmt_fetch($stmt)) { 
     // use $col1 and $col2 
    } 
    */ 
    /* use $col1 and $col2 */ 
    echo "COL1: $col1 COL2: $col2\n"; 
    /* close statement */ 
    mysqli_stmt_close($stmt); 
} 

/* close connection */ 
mysqli_close($link); 
?> 
1

준비된 명령문은 ' 일반 오래된 MySQL/PHP 인터페이스에서 지원됩니다. PDO 또는 mysqli이 필요합니다. 하지만 자리 표시자를 대체하고 싶다면 PHP의 mysql_query 매뉴얼 페이지 check this comment을 이용하면된다.

1

나에게 가장 좋은 해결책 인 mysqli를 사용하려는 경우 codesense_mysqli 클래스 사본을 다운로드하는 것이 좋습니다.

이 래핑과 준비된 문을 사용하는 경우에만

+0

그 클래스는 매우 사용하기 쉽습니다. 나는 왜 이런 것이 더 인기가 없는지 궁금해. – chris

7

내가 동의 이전 MySQL은/​​PHP 인터페이스를 통해 별도의 선이나이 소요되도록 원료 mysqli를 사용할 때 축적 cruft에의 대부분을 숨기는 깔끔한 작은 클래스의 다른 답변 :

  • PHP의 ext/mysql에는 매개 변수가있는 SQL 문이 지원되지 않습니다.
  • 쿼리 매개 변수는 SQL 주입 문제를 방지 할 때보다 안정적인 것으로 간주됩니다.
  • mysql_real_escape_string()도 올바르게 사용하면 효과적 일 수 있지만 코드 작성이 더 까다 롭습니다.
  • 일부 버전에서는 국제 문자 집합이 제대로 이스케이프되지 않은 문자의 경우가 있으므로 미묘한 약점이 있습니다. 쿼리 매개 변수를 사용하면 이러한 경우를 피할 수 있습니다.

매개 변수가 SQL 쿼리의 리터럴 값만 사용하기 때문에 쿼리 매개 변수를 사용하는 경우에도 SQL 주입에 대해주의해야합니다. SQL 쿼리를 동적으로 작성하고 테이블 이름, 열 이름 또는 SQL 구문의 다른 부분에 PHP 변수를 사용하면이 경우 쿼리 매개 변수도 mysql_real_escape_string()도 도움이되지 않습니다. 성능에 관해서는

$query = "SELECT * FROM $the_table ORDER BY $some_column"; 

: 예를 들어

  • 성능 이점은 다른 매개 변수 값으로 준비된 쿼리를 여러 번 실행할 때 온다. 파싱 ​​및 쿼리 준비의 오버 헤드를 피할 수 있습니다. 그러나 동일한 PHP 요청에서 동일한 SQL 쿼리를 얼마나 자주 실행해야합니까?
  • opcode 캐싱이나 데이터 캐싱을 효과적으로 사용하는 것과 같은 성능 향상을 위해 할 수있는 많은 다른 작업에 비해이 성능상의 이점을 활용할 수있는 경우가 많습니다.
  • 준비된 쿼리 이 (가) 성능에 해를 끼치는 경우가 있습니다.다음과 같은 경우에 예를 들어, 최적화 프로그램은 매개 변수 값 을 가정해야하기 때문에이 와일드 카드로 시작 수, 검색을위한 인덱스를 사용할 수 있습니다 가정 할 수의

    SELECT * FROM mytable WHERE textfield LIKE ? 
    
+0

우수 답변. "SELECT * FROM mytable WHERE textfield LIKE?"와 같은 문을 어떻게 처리 할 것을 제안하겠습니까? 준비된 진술이나 다른 것을 사용해야합니까? – chris

+0

그 구체적인 경우, 쿼리 문자열에 패턴을 삽입해야합니다. "SELECT * FROM mytable WHERE textfield LIKE 'word %''". 그런 식으로 옵티마이 저는 인덱스를 사용할 수 있음을 알 수 있습니다. 물론 패턴이 와일드 카드로 시작하면 색인은 아무런 이점도 없습니다. –

관련 문제