这会阻止SQL注入吗?

时间:2011-07-19 18:39:56

标签: php pdo sql-injection

更新1:

我收到以下错误:

Fatal error: Cannot pass parameter 2 by reference in /var/www/page1.php on line 42 Call Stack: 0.0008 341836 1. {main}() /var/www/page1.php:0

使用时:

if( $_SERVER['REQUEST_METHOD'] == 'POST') {
    $fields = array(
        'col1'  => 'cb1',
        'col2'  => 'cb2',
        'col3'  => 'cb3',
        'col4'  => 'cb4',
    );
    $parts = array();
    foreach($fields as $dbfield => $field)
        $parts[] = '`' . $dbfield . '` = :' . $dbfield;
    $dbh = new PDO('mysql:host=localhost;dbname=database', 'user', 'pass');
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sth = $dbh->prepare('UPDATE `table1` SET ' . join(', ', $parts) . ' WHERE `id `= :id');
    // temp simulation value
    $id = 1;
    $sth->bindParam(':id', $id, PDO::PARAM_INT, 4);
    foreach($fields as $dbfield => $field)
        $sth->bindParam(':' . $dbfield, isset($_POST[$field]) ? 1 : 0, PDO::PARAM_INT, 1);
    $sth->execute();
}

原始问题:

以下代码是否会阻止SQL注入?

<?php
    if( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
        // all the way upto 50
        $fields = array('col1'=>'cb1', 'col2'=>'cb2', 'col3'=>'cb3', 'col4'=>'cb4');

        $update = '';

        foreach($fields as $dbfield => $field) {
            if ($update) $update.= ',';

            $update.= ' '.$dbfield.'=';

            $update .= isset($_POST[$field]) ? 1 : 0;
        }

        $DBH = new PDO( "mysql:host=localhost;dbname=database", "user", "pass" );
        $DBH -> setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

        $STH = $DBH -> prepare( "update table1 set " . $update . " where id = :id" );

        // temp simulation value
        $id = 1;

        $STH -> bindParam( ':id', $id, PDO::PARAM_INT, 4 );

        $STH -> execute();
    }
?>

<html>
    <head>
        <title></title>
    </head>

    <body>
        <form method="post">
            <input type="checkbox" name="cb1" />
            <input type="checkbox" name="cb2" />
            <input type="checkbox" name="cb3" />
            <input type="checkbox" name="cb4" />
            <!-- all the way to 50 -->

            <input type="submit" value="submit" />
        </form>
    </body>
</html>

4 个答案:

答案 0 :(得分:3)

嗯,在您的特定情况下,SQL注入无法通过此代码,因为您只是使用预先知道的列名1s和0来构建更新。但是,构建该更新的整体方式在其他情况下重复将是一种非常危险的做法。问题是,你正在使用PDO,但随后迫使它发送一个原始字符串,而不会丢弃你的数据,这会破坏PDO可以做的所有事情来保护你。

为了实际使用PDO针对SQL注入执行的操作,您需要使用?或命名占位符来编写数据,并使用bindParam来提供值。

使用命名参数绑定的动态构造示例可能如下所示:

if( $_SERVER['REQUEST_METHOD'] == 'POST') {
    $fields = array(
        'col1'  => 'cb1',
        'col2'  => 'cb2',
        'col3'  => 'cb3',
        'col4'  => 'cb4',
    );
    $parts = array();
    foreach($fields as $dbfield => $field)
        $parts[] = '`' . $dbfield . '` = :' . $dbfield;
    $dbh = new PDO('mysql:host=localhost;dbname=database', 'user', 'pass');
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sth = $dbh->prepare('UPDATE `table1` SET ' . join(', ', $parts) . ' WHERE `id `= :id');
    // temp simulation value
    $id = 1;
    $sth->bindParam(':id', $id, PDO::PARAM_INT, 4);
    foreach($fields as $dbfield => $field) {
        $value = isset($_POST[$field]) ? 1 : 0;
        $sth->bindParam(':' . $dbfield, $value, PDO::PARAM_INT, 1);
    }
    $sth->execute();
}

答案 1 :(得分:1)

由于您实际上根本没有将外国数据插入到您的表中,我会说您非常安全。 (你应该阅读别人的回答。我想不出每一种可能性。)

但是,我不知道你的代码中的占位符是什么用于简化示例。如果您确实选择使用实际用户数据,那么您应该开始正确使用PDO。截至目前,您只有一个占位符。您的所有字段都没有占位符。您已经有效地破坏了首先使用准备好的查询的目的。

如果您可以描述您想要做的事情,也许我们可以建议一个更清洁的解决方案。

答案 2 :(得分:-1)

这是动态SQL:

 $STH = $DBH -> prepare( "update table1 set " . $update . " where id = :id" ).

除了通过针对已批准值的白名单检查输入$update之外,无法使SQL注入动态SQL。

有关详细信息,请参阅此处:How to prevent SQL injection with dynamic tablenames?

您的代码安全吗? (是)
您的代码会在数组字段中输入值的白名单,如果输入匹配,则会注入1,如果输入匹配,则会注入0
我看到没有SQL注入机会,但它看起来像代码味道。

建议清除代码气味
我个人将表重组为:

Simple_settings
---------------
new_id integer auto_increment primary key 
user_id integer  <<-- optional can be used to check if a user is allowed to alter
                      the setting.
page_id integer  <<-- replaces your id 
settingname varchar(20)
onoff boolean

现在您可以使用普通PDO更新您的表格:

UPDATE table1 onoff = :isfieldset WHERE settingname = :dbfield 
AND page_id = :id

如果你有多个设置,你必须在一个循环中完成它们,但至少你的代码很简单,你的表格不会有很多列。
作为额外的奖励,如果设置数量发生变化,您无需更改表格布局 请注意如果恶意用户将自己的数据注入:dbfield update将失败。
如果你使用它来插入,你的其余代码将不会读取该设置,所以你也应该没问题。

仍有一个问题
如果你有复选框(因为当前用户不允许更改所有设置)你的代码目前没有检查,如果有一些业务逻辑,你需要检查这个用户是允许进入这些设置。您可以在settings_table上的before update触发器中执行此操作,或者对select表使用(先前/已添加)user_allowed_settings,以查看允许哪个用户设置什么设置。

答案 3 :(得分:-2)

不,因为即使您使用的是参数化查询/绑定变量,$update变量仍然是由未经过验证的用户输入组成的。