3

从关于 intertubes 的教程中,我学到了一些关于 PDO 查询的知识。本教程使用了 try/catch,查询的结构基本如下:

try {
    $dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $user, $pass);

    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    $stmt = $dbh->prepare("UPDATE users yada yada yadda");

    $stmt->bindParam(':param1', $param1, PDO::PARAM_INT);
    $stmt->bindParam(':param2', $param2, PDO::PARAM_INT);

    $stmt->execute();

}

catch(PDOException $e)
{
    echo $e->getMessage();
}

这当然会在屏幕上回显 mysql 错误。并不是说我打算进行错误的查询,但我不喜欢在屏幕上直接显示错误的想法,如果攻击者试图诱导所述错误并尝试从中学习一些东西会怎样。

有没有更好的方法来做到这一点,以便任何错误都转到日志文件,或者实际上我在这方面没有什么可担心的,因为绑定参数消除了任何 sql 注入的风险?

4

3 回答 3

4

本教程是正确的,因为您想使用 try..catch 块来捕获可能导致错误的代码并关闭您正在加载的任何内容。因此,如果您有一些依赖于执行此代码的代码,您希望将其包含在您的 try 部分中。如果您绝对需要此代码来执行您正在创建的任何工作,那么您可能想要捕获错误并将用户重定向到某种类型的错误页面。

如果您使用php 错误日志功能,那么而不是

echo $e->getMessage();

您可以使用

error_log($e->getMessage(),0);

将错误消息从 PDO 直接发送到您的 php 错误日志。如果您不知道错误日志在哪里,如果您正在运行 *nix 系统,可以查看此链接以获取指向它的几个指针。如果您正在运行 Windows,则应该有一个配置文件会告诉您。或者您可以检查 php ini 文件以获取它指向的位置,以找到找到日志的可靠方法。

于 2013-05-07T15:52:01.020 回答
1

有一个更好的方法吗

是的,当然!

这显然是本教程教给您的处理 PDO 错误的错误方法。
所以,只要摆脱这些try..catch命令 - 就是这样。

这样,您就可以像处理其他 PHP 错误一样处理 PDO 异常。因此,如果出现查询错误,您的脚本将暂停并记录错误(如果您告诉 PHP)。
要告诉 PHP,您必须将log_errorsini 指令设置为1 要告诉 PHP 不要在屏幕上显示错误,请将display_errorsini 指令设置为0(在开发服务器上,您可能希望反转它们)

于 2013-05-07T15:28:26.627 回答
0

Well, my answer probably not in the best practice, so please leave it to the last option. But for my case, it works perfectly.

PDO::__construct however will give you an exception anyway no matter what you set in PDO::ATTR_ERRMODE. I don't know why they design it to behave like that.

My way to solve this problem is to create a code area i call it Debug Critical Section (means you need very careful about the codes in the section), any errors in this section will not directly output to user.

Here is the code i made for my framework:

private function doPDOConnect($dbIndex, &$DBInfo, &$error) {
    $dbh = null;
    $successed = false;

    if (!isset($this->connectedDB[$dbIndex])) {

        // Enter Critical Section so no error below belowing code will cause error output, but the error still in log though
        facula::core('debug')->criticalSection(true);

        try {
            $dbh = new PDO($DBInfo['Driver'] . ':' . $DBInfo['Connection'] . '=' . $DBInfo['Host'] . ';dbname=' . $DBInfo['Database'], $DBInfo['Username'], $DBInfo['Password'], array( PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING, PDO::ATTR_TIMEOUT => $DBInfo['Timeout'] )); // ATTR_ERRMODE => PDO::ERRMODE_WARNING. or we will cannot get anything even error happens

            $dbh->facula_prefix = $DBInfo['Prefix'];
            $dbh->facula_index = $dbIndex;
            $dbh->facula_connection = $DBInfo; // In order you want to reconnect this specify database after connection lost etc, remove if you worry about the security issue.

            $successed = true;
        } catch (PDOException $e) {
            $error = $e->getMessage(); // If any error, catch it, to &$error.
        }

        // Exit Critical Section, restore error caught
        facula::core('debug')->criticalSection(false);

        if ($successed) {
            return $this->connectedDB[$dbIndex] = $dbh;
        }
    } else {
        return $this->connectedDB[$dbIndex];
    }

    return false;
}

So in your case, you may replace my facula::core('debug')->criticalSection to display_errors off/on to handle the error display handler correctly.

For example:

$display_error_status = ini_get('display_errors');

function criticalSection($entered) {
    global $display_error_status;

    if ($entered) {
        ini_set('display_errors', '0');
    } else {
        ini_set('display_errors', $display_error_status);
    }
}
于 2013-05-16T13:58:20.170 回答