【问题标题】:Need help making PDO prepared statements more secure需要帮助使 PDO 准备好的语句更安全
【发布时间】:2015-12-08 06:41:01
【问题描述】:

我正在用 PHP 构建一个自定义 CMS,并且我想从一个数组生成 MySQL。我猜这不是最安全的做法,因为我会将变量直接放入准备好的语句中。我希望更有经验的人可以看看我到目前为止所做的事情,并让我了解如何使这更安全。

这是我存储所有数据操作方法的类

abstract class MySQLData extends MySQLDataHelper {

    // connection to database
    abstract public function getConn();
    // table name from concrete class
    abstract public function getTable();

    public function insert( $args ) {
        try {
            $stmt = $this->getConn()->prepare( $this->generateInsertSQL( $args ) );

            foreach ($args as $arg => &$value) {
                $stmt->bindParam(":{$arg}", $value);
            }

            $stmt->execute();               
        } catch (PDOException $e) {
            echo "Insert error: " . $e->getMessage();
        }
    }
//............................................
// More methods below, including delete, select and update, but lets focus on 'insert' only to keep things simple.
}

Helper方法类,包括插入SQL生成器

abstract class MySQLDataHelper {

    public function generateInsertSQL( $args ) {
        $columns = array_keys( $args );

        $placeholders = implode(', :', $columns);
        $columns = implode(', ', $columns);

        return "INSERT INTO `{$this->getTable()}` ({$columns}) VALUES (:{$placeholders})";
    }       
}

示例用例

require_once 'core/Pages.php';
$pages = new Pages( $db->conn() );  // Pages extends MySQLData

$args = array(
    'name'      => 'New page',
    'content'   => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam, laboriosam!',
    'enabled'   => true);

$pages->insert( $args );

请注意,在这种情况下 generateInsertSQL 的输出将是:

INSERT INTO `pages` (name, content, enabled) VALUES (:name, :content, :enabled)

提醒,这段代码运行良好,我只是想知道它是否可以变得更安全。

谢谢。如果这是一个不好的问题或在错误的地方,我很抱歉。

【问题讨论】:

  • $args 会来自用户输入吗?
  • “这段代码工作得很好,我想知道它是否可以变得更安全。” - 不要上网。如果适用,请考虑 XSS 注入。
  • 如何更安全?除非你做一些愚蠢的事情,比如$args['\'; drop table students; --'] = 'haha im in ur query injecting ur tablez'...
  • 谢谢你们。 @Rocket Hazmat,是的,这将是来自管理面板中表单的用户输入。请注意,我当然会清理该输入,所以它更像是: $args = array('name' => filterInput($_POST['name']));
  • 你考虑过使用RedBean吗?我强烈推荐它。我曾经担心 SQL 注入和许多其他问题,直到我发现通过 ORM 可以更轻松地进行数据库操作,并且不太容易出现开发人员错误。

标签: php mysql oop pdo


【解决方案1】:

这是一个社区 wiki 答案。任何人都可以对其进行编辑以加入。

绑定变量解决的问题是:SQL 是一种语言,当您通过插入数据来组装该语言中的语句时,就像它是文本一样,您为不受信任的人提供了一种增强您的 SQL 程序的方法。

因此,您必须认真消毒所有内容。例如,如果你正在做这样的事情:

 id IN (4 5,6,7) 

确保所有数字都是实际数字并不难。但是您必须每次都这样做,不要失败,否则会被打败。另一方面,如果你正在做

name IN ('cyanographics', 'Rocket Hazmat', 'Fred -ii-1')

有人给你一个恶意的用户名——想想

    cyano'); DELETE FROM TABLE USER CASCADE CONSTRAINTS; //

您最终要求您的 SQL 语言解释器运行它。

name IN ('cyano'); DELETE FROM TABLE users CASCADE CONSTRAINTS; //', 'Rocket Hazmat', 'Fred -ii-1')

这种事情可能会使您的应用程序变得一团糟。

因此,您需要仔细考虑如何清理文本输入。您可以考虑使用PDO::quote() 之类的方式清理文本字符串。它在字符串中的危险字符(如')周围放置转义序列。

但是,您最好使用 PDO 中的内置绑定变量支持,即使处理您想要处理的列表变得更加困难。

阅读:What are the best PHP input sanitizing functions?

另外,不要忘记考虑浏览器脚本注入。如果你得到了名字怎么办

 <script>alert('You are pwned, sucka!');</script>

还是更糟糕的事情?你也需要从你的输入中清除那些东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-21
    • 2012-08-12
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    相关资源
    最近更新 更多