program tip

PDO MySQL : PDO :: ATTR_EMULATE_PREPARES를 사용합니까?

radiobox 2020. 7. 29. 08:06
반응형

PDO MySQL : PDO :: ATTR_EMULATE_PREPARES를 사용합니까?


이것이 내가 지금까지 읽은 것입니다 PDO::ATTR_EMULATE_PREPARES.

  1. MySQL의 기본 준비는 쿼리 캐시를 무시하므로 PDO의 준비 에뮬레이션은 성능에 더 좋습니다 .
  2. MySQL의 기본 준비는 보안을 위해 더 좋습니다 (SQL Injection 방지) .
  3. MySQL의 기본 준비는 오류보고에 더 좋습니다 .

나는이 진술들 중 어느 것이 더 이상 참인지 모르겠다. MySQL 인터페이스를 선택할 때 가장 큰 관심사는 SQL 인젝션을 방지하는 것입니다. 두 번째 관심사는 성능입니다.

내 응용 프로그램은 현재 절차 적 MySQLi (준비된 명령문 없음)를 사용하며 쿼리 캐시를 상당히 활용합니다. 단일 요청에서 준비된 명령문을 거의 재사용하지 않습니다. 명명 된 매개 변수와 준비된 명령문의 보안을 위해 PDO로 이동하기 시작했습니다.

나는 사용 MySQL 5.1.61하고있다PHP 5.3.2

PDO::ATTR_EMULATE_PREPARES활성화 상태로 두어야 합니까? 쿼리 캐시의 성능과 준비된 명령문의 보안을 모두 유지할 수있는 방법이 있습니까?


우려 사항에 답변하려면 다음과 같이하십시오.

  1. MySQL> = 5.1.17 ( PREPARE및 and EXECUTE문의 경우> = 5.1.21 ) 은 쿼리 캐시에서 준비된 명령문을 사용할 수 있습니다 . 따라서 MySQL + PHP 버전은 쿼리 캐시와 함께 준비된 명령문을 사용할 수 있습니다. 그러나 MySQL 문서에서 쿼리 결과를 캐싱 할 때주의해야 할 사항에주의하십시오. 캐시 할 수 없거나 캐시 된 경우에도 쓸모없는 쿼리에는 여러 종류가 있습니다. 내 경험상 쿼리 캐시는 종종 큰 승리는 아닙니다. 쿼리와 스키마는 캐시를 최대한 활용하기 위해 특별한 구성이 필요합니다. 어쨌든 응용 프로그램 수준 캐싱은 결국 장기적으로 필요할 수 있습니다.

  2. 기본 준비는 보안에 아무런 영향을 미치지 않습니다. 의사가 준비한 명령문은 여전히 ​​쿼리 매개 변수 값을 이스케이프합니다. 이진 프로토콜을 사용하는 MySQL 서버 대신 문자열이있는 PDO 라이브러리에서 수행됩니다. 즉, 동일한 PDO 코드는 EMULATE_PREPARES설정에 관계없이 주입 공격에 동일하게 취약하거나 취약하지 않습니다 . 유일한 차이점은 매개 변수 교체가 EMULATE_PREPARES발생하는 위치입니다.로 PDO 라이브러리에서 발생합니다. 이 없으면 EMULATE_PREPARESMySQL 서버에서 발생합니다.

  3. EMULATE_PREPARES그렇지 않으면 실행 시간이 아닌 준비 시간에 구문 오류가 발생할 수 있습니다. EMULATE_PREPARESPDO는 실행 시간까지 MySQL로 제공하는 쿼리를 가지고 있지 않기 때문에에만 실행시 구문 오류를 얻을 것이다. 참고 이것은 당신이 쓸 것 코드에 영향을 미친다 ! 특히 당신이 사용하는 경우 PDO::ERRMODE_EXCEPTION!

추가 고려 사항 :

  • prepare()(기본 준비된 명령문 사용 )에는 고정 비용이 있으므로 prepare();execute()기본 준비된 명령문을 사용하면 에뮬레이트 된 준비된 명령문을 사용하여 일반 텍스트 쿼리를 실행하는 것보다 약간 느릴 수 있습니다. 많은 데이터베이스 시스템에서 a에 대한 쿼리 계획 prepare()도 캐시되어 있으며 여러 연결과 공유 될 수 있지만 MySQL은 그렇게 생각하지 않습니다. 따라서 여러 쿼리에 준비된 문 개체를 재사용하지 않으면 전체 실행 속도가 느려질 수 있습니다.

최종 권장 사항으로 , 이전 버전의 MySQL + PHP에서는 준비된 명령문을 에뮬레이트해야하지만 최신 버전에서는 에뮬레이션을 해제해야합니다.

PDO를 사용하는 몇 가지 앱을 작성한 후 최고의 설정이라고 생각되는 PDO 연결 기능을 만들었습니다. 아마도 이와 같은 것을 사용하거나 원하는 설정으로 조정해야합니다.

/**
 * Return PDO handle for a MySQL connection using supplied settings
 *
 * Tries to do the right thing with different php and mysql versions.
 *
 * @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
 * @return PDO
 * @author Francis Avila
 */
function connect_PDO($settings)
{
    $emulate_prepares_below_version = '5.1.17';

    $dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
    $dsnarr = array_intersect_key($settings, $dsndefaults);
    $dsnarr += $dsndefaults;

    // connection options I like
    $options = array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    );

    // connection charset handling for old php versions
    if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
    }
    $dsnpairs = array();
    foreach ($dsnarr as $k => $v) {
        if ($v===null) continue;
        $dsnpairs[] = "{$k}={$v}";
    }

    $dsn = 'mysql:'.implode(';', $dsnpairs);
    $dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);

    // Set prepared statement emulation depending on server version
    $serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    $emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);

    return $dbh;
}

PDO::ATTR_EMULATE_PREPARESPHP pdo_mysql가 컴파일되지 않은 경우 비활성화 (기본 설정 켜기) 에주의하십시오 mysqlnd.

old libmysql는 일부 기능과 완벽하게 호환되지 않으므로 이상한 버그가 발생할 수 있습니다. 예를 들면 다음과 같습니다.

  1. 다음과 같이 바인딩 할 때 64 비트 정수에 대한 최상위 비트 손실 PDO::PARAM_INT(64 비트 컴퓨터에서 0x12345678AB가 0x345678AB로 잘림)
  2. 간단한 쿼리를 수행 할 수 없음 LOCK TABLES( SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet예외 발생)
  3. Need to fetch all rows from result or close cursor before next query ( with mysqlnd or emulated prepares it automatically does this work for you and doesn't go out of sync with mysql server )

These bugs I figured out in my simple project when migrated to other server which used libmysql for pdo_mysql module. Maybe there are much more bugs, I don't know. Also I tested on fresh 64bit debian jessie, all listed bugs occur when I apt-get install php5-mysql, and disappear when I apt-get install php5-mysqlnd.

When PDO::ATTR_EMULATE_PREPARES is set to true (as default) - these bugs don't happen anyway, because PDO doesn't use prepared statements at all in this mode. So, if you use pdo_mysql based on libmysql ("mysqlnd" substring does't appear in "Client API version" field of pdo_mysql section in phpinfo) - you should not turn PDO::ATTR_EMULATE_PREPARES off.


I would turn off emulate prepares as you're running 5.1 which means PDO will take advantage of the native prepared statement functionality.

PDO_MYSQL will take advantage of native prepared statement support present in MySQL 4.1 and higher. If you're using an older version of the mysql client libraries, PDO will emulate them for you.

http://php.net/manual/en/ref.pdo-mysql.php

I ditched MySQLi for PDO for the prepared named statements and the better API.

However, to be balanced, PDO performs negligibly slower than MySQLi, but it's something to bear in mind. I knew this when I made the choice, and decided that a better API and using the industry standard was more important than using a negligibly faster library that ties you to a particular engine. FWIW I think the PHP team is also looking favourably at PDO over MySQLi for the future too.


I'd recommend enabling real database PREPARE calls as the emulation doesn't catch everything.., for example, it will prepare INSERT;!

var_dump($dbh->prepare('INSERT;'));
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
var_dump($dbh->prepare('INSERT;'));

The output

object(PDOStatement)#2 (1) {
  ["queryString"]=>
  string(7) "INSERT;"
}
bool(false)

I'll gladly take a performance hit for code that actually works.

FWIW

PHP Version: PHP 5.4.9-4ubuntu2.4 (cli)

MySQL Version: 5.5.34-0ubuntu0


Why switch emulation to ‘false’?

The main reason for this is that having the database engine do the prepare instead of PDO is that the query and the actual data are sent separately, which increases security. This means when the parameters are passed to the query, attempts to inject SQL into them are blocked, since MySQL prepared statements are limited to a single query. That means that a true prepared statement would fail when passed a second query in a parameter.

The main argument against using the database engine for the prepare vs PDO is the two trips to the server – one for the prepare, and another for the parameters to get passed – but I think the added security is worth it. Also, at least in the case of MySQL, query caching has not been an issue since version 5.1.

https://tech.michaelseiler.net/2016/07/04/dont-emulate-prepared-statements-pdo-mysql/

참고URL : https://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not

반응형