개발 꿀팁/PHP

PDO 쿼리mysql을 사용하여 SQL 주입을 피함

Jammie 2022. 8. 17. 16:43
반응형

기존의 mysql_connect, mysql_query 방법을 사용하여 쿼리 데이터베이스를 접속할 경우 필터링이 잘 되지 않으면 SQL 주입 위험이 있습니다.사용자가 제출한 값은 mysql_real_escape_string() 함수로 필터링할 수 있지만 결함도 있다.PHP의 PDO 확장 prepare 방법을 사용하면 sql injection 리스크를 피할 수 있습니다.

PDO(PHP Data Object)는 PHP5 이전의 php4/php3는 각각 php_mysql.dll과 같은 데이터베이스와 연결 및 처리를 위해 확장되었기 때문에 PHP5에 새로 추가된 중요한 기능입니다. PHP6에서도 기본적으로 PDO 방식으로 연결되며, mysql 확장을 보조로 사용합니다

 

1. PDO 설정

PDO 확장을 사용하기 전에먼저 이 확장을 활성화하려면 php.ini에서 "extension=php_pdo.dll" 앞에 있는;" 번호를 제거하고, 데이터베이스에 접속하려면 PDO 관련 데이터베이스 확장 앞에 있는;" 번호(일반적으로 php_pdo_mysql.dll)를 삭제한 후 Apache 서버를 재시작하면 됩니다

extension=php_pdo.dll 
extension=php_pdo_mysql.dll

2. PDO 연결mysql 데이터베이스

$dbh = new PDO("mysql:host=localhost;dbname=mydb","root","password");

기본적으로 긴 연결은 아닙니다. 긴 연결을 사용하려면 다음 매개 변수를 입력하십시오

$dbh = new PDO("mysql:host=localhost;dbname=mydb","root","password","array(PDO::ATTR_PERSISTENT => true) "); 
$dbh = null; //(석방하다)

. PDO 속성 설정
PDO에는 세 가지 오류가 있습니다방식:

PDO::ERrmODE_SILENT 에러 없음정보, 오류 코드만 설정

PDO::ERrmODE_WARNING 경고 표시틀리다.

PDO::ERrmODE_EXCEPTION 드롭이상하다.

다음 문장으로 설정할 수 있습니다오류처리방식은 드롭아웃 이상

$db->setAttribute(PDO::ATTR_ERrmODE, PDO::ERrmODE_EXCEPTION);

데이터베이스에 따라 반환되는 필드 이름의 대소문자 처리가 다르기 때문에 PDO::ATTR_CASE 설정 항목(PDO::CASE_LOWER, PDO::CASE_NATURAL, PDO:::CASE_UPPER 포함)을 제공하여 반환되는 필드 이름의 대소문자를 결정합니다.
PDO::ATTR_ORACLE_NULLS 유형(PDO::NULL_NATURAL, PDO::NULL_EmpTY_STRING, PDO:::NULL_TO_STRING 포함)을 설정하여 데이터베이스에서 반환되는 NULL 값이 php에 해당하는 값을 지정합니다.


4. PDO 상용방법 및 그 응용

PDO::query( )는 주로 기록된 결과를 반환하는 작업, 특히 SELECT 작업에 사용됩니다.

PDO::exec()은 INSERT, UPDATE와 같은 결과 집합이 없는 작업에 대한 것입니다.

PDO:: prepare( )는 주로 전처리 작업입니다. $rs ->execute( )를 통해 전처리된 SQL 문을 실행해야 합니다. 이 방법은 인수를 바인딩할 수 있어 강력한 기능을 제공합니다. (sql 주입을 방지하는 것은 이 방법입니다.)

PDO:: lastInsertId() 마지막 삽입 작업을 되돌립니다. 주 키 열 유형은 자체 증가의 마지막 자체 증가 ID입니다.

PDOStatement::fetch( )는 레코드를 가져오는 데 사용됩니다

PDOStatement::fetchAll( ) 은 모든 레코드를 모음으로 가져옵니다

PDOStatement::fetchColumn()은 첫 번째 레코드의 필드를 지정합니다. 기본적으로 첫 번째 필드입니다.

PDOStatement:::rowCount( ) : PDO:::query()와 PDO::prepare( )에서 DELEETE, INSERT, UPDATE 동작에 영향을 준 결과 세트에 주로 사용되며 PDO::exec() 방법과 SELECT 동작에는 유효하지 않습니다.

5.PDO 작업MYSQL 데이터베이스 인스턴스

<?php 
$pdo = new PDO("mysql:host=localhost;dbname=mydb","root",""); 
if($pdo -> exec("insert into mytable(name,content) values('fdipzone','123456')")){ 
echo "insert success"; 
echo $pdo -> lastinsertid(); 
} 
?>
<?php 
$pdo = new PDO("mysql:host=localhost;dbname=mydb","root",""); 
$rs = $pdo -> query("select * from table"); 
$rs->setFetchMode(PDO::FETCH_ASSOC); //연관 배열 형식
//$rs->setFetchMode(PDO::FETCH_NUM); //디지털 인덱스 배열 형식
while($row = $rs -> fetch()){ 
    print_r($row); 
} 
?>
<?php
foreach( $db->query( "SELECT * FROM table" ) as $row )
{
    print_r( $row );
}
?>

몇 줄의 데이터가 있는지 집계합니다

<?php
$sql="select count(*) from table";
$num = $dbh->query($sql)->fetchColumn();
?>

prepare 방식:

<?php
$query = $dbh->prepare("select * from table");
if ($query->execute()) {
    while ($row = $query->fetch()) {
        print_r($row);
    }
}
?>

prepare 매개 변수화 쿼리:

<?php
$query = $dbh->prepare("select * from table where id = ?");
if ($query->execute(array(1000))) { 
    while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
        print_r($row);
    }
}
?>

PDO를 사용하여 MySQL 데이터베이스에 접속할 경우, 실제 real prepared statements는 기본적으로 사용되지 않습니다.이 문제를 해결하려면 prepared statements 에뮬레이션 효과를 비활성화해야 합니다.다음은 PDO를 사용하여 링크를 만드는 예입니다

<?php
$dbh = new PDO('mysql:dbname=mydb;host=127.0.0.1;charset=utf8', 'root', 'pass');
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
?>

setAttribute() 행은 필수입니다. 이것은 PDO에게 전처리문 시뮬레이션을 사용하지 말라고 지시하고 real parepared statements를 사용합니다.이를 통해 SQL 문장과 해당 값이 mysql 서버로 전달될 때까지 PHP에 의해 해석되지 않도록 할 수 있다(모든 악의적인 SQL 주입 공격을 금지한다).
문자 집합의 속성을 설정할 수 있지만 (charset=utf8) 이전 버전의 PHP (< 5.3.6)는 DSN에서 문자 매개 변수를 무시합니다.



전체 코드 사용 예제:

<?php
$dbh = new PDO("mysql:host=localhost; dbname=mydb", "root", "pass");
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //사용 안 함prepared statements에뮬레이션 효과
$dbh->exec("set names 'utf8'"); 
$sql="select * from table where username = ? and password = ?";
$query = $dbh->prepare($sql); 
$exeres = $query->execute(array($username, $pass)); 
if ($exeres) { 
    while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
        print_r($row);
    }
}
$dbh = null;
?>

sql 주입에 대비할 수 있는 코드다.왜 그럴까?
prepare() 가 호출되었을 때 쿼리 스테이트먼트를 보냈습니다데이터베이스 서버로 보내졌습니다. 자리 표시자만 있습니까? 과거에는 사용자가 제출한 데이터가 없습니다. execute()로 호출될 때 사용자가 제출한 값이 데이터베이스에 전달됩니다. 이것들은 분리되어 전송됩니다. 둘 다 독립적이어서 SQL 공격자가 기회를 얻을 수 없습니다.



그러나 PDO는 그렇지 않다.SQL 주입에 대비할 수 있도록 도와줍니다.

플레이스 홀더를 사용할 수 없습니다? 한 조의 값을 대신해서, 이렇게 하면 그것만 얻을 수 있다.그룹 데이터의 첫 번째 값(예:

select * from table where userid in ( ? );

in으로 찾으려면 find_in_set()로 바꾸면 됩니다

$ids = '1,2,3,4,5,6';
select * from table where find_in_set(userid, ?);

테이블 이름이나 열 이름 대신 자리 표시자를 사용할 수 없습니다

select * from table order by ?;

플레이스 홀더를 사용할 수 없습니다? 다음과 같은 다른 SQL 문법을 대체합니다

select extract( ? from addtime) as mytime from table;
반응형