PHP PDO 문이 테이블 또는 열 이름을 매개 변수로 받아 들일 수 있습니까?

StackOverflow https://stackoverflow.com/questions/182287

  •  06-07-2019
  •  | 
  •  

문제

테이블 이름을 준비된 PDO 문에 전달할 수없는 이유는 무엇입니까?

$stmt = $dbh->prepare('SELECT * FROM :table WHERE 1');
if ($stmt->execute(array(':table' => 'users'))) {
    var_dump($stmt->fetchAll());
}

테이블 이름을 SQL 쿼리에 삽입하는 또 다른 안전한 방법이 있습니까? 안전하고 나는하고 싶지 않다는 것을 의미합니다

$sql = "SELECT * FROM $table WHERE 1"
도움이 되었습니까?

해결책

표와 열 이름은 PDO의 매개 변수로 대체 할 수 없습니다.

이 경우 단순히 데이터를 수동으로 필터링하고 소독 할 것입니다. 이를 수행하는 한 가지 방법은 속기 매개 변수를 쿼리를 동적으로 실행 한 다음 A를 사용하는 기능으로 전달하는 것입니다. switch() 표 이름 또는 열 이름에 사용할 유효한 값의 흰색 목록을 작성하는 문. 그렇게하면 사용자 입력이 쿼리에 직접 들어가는 적이 없습니다. 예를 들어 :

function buildQuery( $get_var ) 
{
    switch($get_var)
    {
        case 1:
            $tbl = 'users';
            break;
    }

    $sql = "SELECT * FROM $tbl";
}

기본 케이스를 남기지 않거나 오류 메시지를 반환하는 기본 케이스를 사용하여 사용하려는 값 만 사용하는지 확인하십시오.

다른 팁

이해하다 테이블 (또는 열) 이름을 바인딩하는 것이 작동하지 않으므로 준비된 진술의 자리 표시자가 어떻게 작동하는지 이해해야합니다. 단순히 (적절하게 탈출 한) 문자열로 대체되지 않고 결과 SQL이 실행되었습니다. 대신, DBMS는 성명서를 "준비"하도록 요청했습니다. 진술서는 자리 표시자를 채우는 방법에 관계없이 사용되는 테이블 및 색인을 포함하여 해당 쿼리를 어떻게 실행하는지에 대한 완전한 쿼리 계획을 제시합니다.

계획 SELECT name FROM my_table WHERE id = :value 당신이 대체하는 것과 동일합니다 :value, 그러나 겉보기에는 비슷합니다 SELECT name FROM :table WHERE id = :value DBMS는 실제로 어떤 테이블을 선택할 것인지 전혀 모르기 때문에 계획 할 수 없습니다.

PDO와 같은 추상화 라이브러리는 준비된 진술의 두 가지 주요 목적을 물리 칠 수 있기 때문에 다음과 같은 추상화 도서관이 아닙니다. 1) 데이터베이스가 쿼리가 어떻게 실행되는지 미리 결정하고 동일하게 사용하도록 허용합니다. 여러 번 계획하십시오. 2) 변수 입력에서 쿼리의 논리를 분리하여 보안 문제를 방지합니다.

나는 이것이 오래된 게시물이라는 것을 알았지 만 그것이 유용하다고 생각했고 @kzqai가 제안한 것과 유사한 솔루션을 공유 할 것이라고 생각했습니다.

두 개의 매개 변수를 수신하는 함수가 있습니다.

function getTableInfo($inTableName, $inColumnName) {
    ....
}

내부는 어레이에 대해 확인하여 "축복받은"테이블이있는 테이블과 열만 액세스 할 수 있는지 확인하기 위해 설정했습니다.

$allowed_tables_array = array('tblTheTable');
$allowed_columns_array['tblTheTable'] = array('the_col_to_check');

그런 다음 PDO를 실행하기 전에 PHP 점검은 ...

if(in_array($inTableName, $allowed_tables_array) && in_array($inColumnName,$allowed_columns_array[$inTableName]))
{
    $sql = "SELECT $inColumnName AS columnInfo
            FROM $inTableName";
    $stmt = $pdo->prepare($sql); 
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}

전자를 사용하는 것은 본질적으로 후자보다 더 안전하지 않으므로 매개 변수 배열의 일부인지 간단한 변수의 일부인지 입력을 소독해야합니다. 그래서 후자의 형태를 사용하는 데 아무런 문제가 없습니다. $table, 당신이 당신의 내용을 확인해야한다면 $table 그것을 사용하기 전에 안전 (알파늄 + 밑줄?)입니다.

(늦은 답변, 내 부수적 참고에 문의하십시오).

"데이터베이스"를 만들 때 동일한 규칙이 적용됩니다.

준비된 명령문을 사용하여 데이터베이스를 바인딩 할 수 없습니다.

즉:

CREATE DATABASE IF NOT EXISTS :database

작동 안 할 것이다. 대신 SAFELIST를 사용하십시오.

사이드 참고 : 나는이 답을 (커뮤니티 위키로) 추가했기 때문에 종종 질문을 닫는 데 사용 되었기 때문에 일부 사람들은 묶기 위해 이것과 비슷한 질문을 게시했습니다. 데이터 베이스 테이블 및/또는 열이 아닙니다.

나 중 일부는 자신의 맞춤형 소독 기능을 간단하게 제공 할 수 있는지 궁금합니다.

$value = preg_replace('/[^a-zA-Z_]*/', '', $value);

나는 그것을 통해 정말로 생각하지 않았지만 캐릭터와 밑줄을 제외하고는 작동하는 것을 제거하는 것처럼 보입니다.

이 스레드의 주요 질문에 관해서는, 다른 게시물은 진술을 준비 할 때 값을 열 이름에 묶을 수없는 이유를 분명히 했으므로 다음은 다음과 같습니다.

class myPdo{
    private $user   = 'dbuser';
    private $pass   = 'dbpass';
    private $host   = 'dbhost';
    private $db = 'dbname';
    private $pdo;
    private $dbInfo;
    public function __construct($type){
        $this->pdo = new PDO('mysql:host='.$this->host.';dbname='.$this->db.';charset=utf8',$this->user,$this->pass);
        if(isset($type)){
            //when class is called upon, it stores column names and column types from the table of you choice in $this->dbInfo;
            $stmt = "select distinct column_name,column_type from information_schema.columns where table_name='sometable';";
            $stmt = $this->pdo->prepare($stmt);//not really necessary since this stmt doesn't contain any dynamic values;
            $stmt->execute();
            $this->dbInfo = $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
    }
    public function pdo_param($col){
        $param_type = PDO::PARAM_STR;
        foreach($this->dbInfo as $k => $arr){
            if($arr['column_name'] == $col){
                if(strstr($arr['column_type'],'int')){
                    $param_type = PDO::PARAM_INT;
                    break;
                }
            }
        }//for testing purposes i only used INT and VARCHAR column types. Adjust to your needs...
        return $param_type;
    }
    public function columnIsAllowed($col){
        $colisAllowed = false;
        foreach($this->dbInfo as $k => $arr){
            if($arr['column_name'] === $col){
                $colisAllowed = true;
                break;
            }
        }
        return $colisAllowed;
    }
    public function q($data){
        //$data is received by post as a JSON object and looks like this
        //{"data":{"column_a":"value","column_b":"value","column_c":"value"},"get":"column_x"}
        $data = json_decode($data,TRUE);
        $continue = true;
        foreach($data['data'] as $column_name => $value){
            if(!$this->columnIsAllowed($column_name)){
                 $continue = false;
                 //means that someone possibly messed with the post and tried to get data from a column that does not exist in the current table, or the column name is a sql injection string and so on...
                 break;
             }
        }
        //since $data['get'] is also a column, check if its allowed as well
        if(isset($data['get']) && !$this->columnIsAllowed($data['get'])){
             $continue = false;
        }
        if(!$continue){
            exit('possible injection attempt');
        }
        //continue with the rest of the func, as you normally would
        $stmt = "SELECT DISTINCT ".$data['get']." from sometable WHERE ";
        foreach($data['data'] as $k => $v){
            $stmt .= $k.' LIKE :'.$k.'_val AND ';
        }
        $stmt = substr($stmt,0,-5)." order by ".$data['get'];
        //$stmt should look like this
        //SELECT DISTINCT column_x from sometable WHERE column_a LIKE :column_a_val AND column_b LIKE :column_b_val AND column_c LIKE :column_c_val order by column_x
        $stmt = $this->pdo->prepare($stmt);
        //obviously now i have to bindValue()
        foreach($data['data'] as $k => $v){
            $stmt->bindValue(':'.$k.'_val','%'.$v.'%',$this->pdo_param($k));
            //setting PDO::PARAM... type based on column_type from $this->dbInfo
        }
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);//or whatever
    }
}
$pdo = new myPdo('anything');//anything so that isset() evaluates to TRUE.
var_dump($pdo->q($some_json_object_as_described_above));

위의 내용은 단지 예일 뿐이므로 말할 것도없이 카피-> 페이스트는 작동하지 않습니다. 필요에 맞게 조정하십시오. 이제 100% 보안을 제공하지는 않지만 동적 문자열로 "들어오고"사용자 끝에서 변경 될 때 열 이름을 제어 할 수 있습니다. 또한 information_schema에서 추출되기 때문에 테이블 열 이름과 유형이있는 배열을 빌드 할 필요가 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top