1

可能重复:
如何防止 PHP 中的 SQL 注入?

我想问一些关于防止 sql 注入的问题。从我一直在阅读的内容中,我不断遇到以下三件事:

  • 带斜杠
  • 与magic_quotes_gpc一起使用
  • mysql_real_escape_string (或我想在较新的 php 中的 mysqli?)

问题是,我应该同时使用这两种方法还是 real_escape_string 就足够了?

例如,我有这行与注册页面有关的代码(我知道事实上它很容易受到攻击,因为 sqli 助手让我可以找到关于我的数据库的所有内容:(因为我还没有实现上述任何内容):

    if(isset($_POST['submit'])){
    //cleanup the variables
    $username = ($_POST['username']);
    $password = ($_POST['password']);
    $email = ($_POST['email']);
    $username = sanitise($username);
    $password = sanitise($password);
    $email = sanitise($email);
    //quick/simple validation
    if(empty($username)){ $action['result'] = 'error'; array_push($text,'Please type in a username'); }
    if(empty($password)){ $action['result'] = 'error'; array_push($text,'Please type in a password'); }
if($action['result'] != 'error'){
        $password = md5($password); 
        //add to the database
        $add = mysql_query("INSERT INTO Users VALUES(NULL,'$username','$password','$email',0, 'First', 'Last', 'Phone Number Here', '...', 'Something about me...')");
        if($add){
            //get the new user id
            $userid = mysql_insert_id();    
            //create a random key
            $key = $username . $email . date('mY');
            $key = md5($key);
            //add confirm row
            $confirm = mysql_query("INSERT INTO Confirm VALUES(NULL,'$userid','$key','$email')");   
            if($confirm){
                //include the swift class
                include_once 'swift/swift_required.php';
                //put info into an array to send to the function
                $info = array(
                    'username' => $username,
                    'email' => $email,
                    'key' => $key);
                //send the email
                if(send_email($info)){
                    //email sent
                    $action['result'] = 'success';
                    array_push($text,'Thanks for signing up. Please check your e-mail for confirmation.');
                }else{
                    $action['result'] = 'error';
                    array_push($text,'Could not send confirmation e-mail');
                }
            }else{
                $action['result'] = 'error';
                array_push($text,'Confirm row was not added to the database. Reason: ' . mysql_error());
            }
        }else{
            $action['result'] = 'error';
            array_push($text,'User could not be added to the database. Reason: ' . mysql_error());
        }
    }
    $action['text'] = $text;
}
?>

我认为我的消毒功能会有所帮助 - 把它放到网上,但它看起来有点没用。或者它可能只有助于防止跨站点脚本。这里是:

 function cleanInput($input) {
        $search = array(
            '@<script[^>]*?>
.*?</script>@si',   // Strip out javascript
    '@<[\/\!]*?[^<>]*?>@si',            // Strip out HTML tags
    '@<style[^>]*?>.*?
    </style>
@siU',    // Strip style tags properly
    '@<![\s\S]*?--[ \t\n\r]*>@'         // Strip multi-line comments
    );
    $output = preg_replace($search, '', $input);
    return $output;
    }
function sanitise($input) {
    if (is_array($input)) {
    foreach($input as $var=>$val) {
    $output[$var] = sanitise($val);
    }
    }
    else {
    if (get_magic_quotes_gpc()) {
    $input = stripslashes($input);
    }
    $input  = cleanInput($input);
    $output = $input;
    }
    return $output;
}

你会建议这个功能没用吗?

如果是这样,我将如何保护原始代码?即:

    $username = ($_POST['username']);
    $password = ($_POST['password']);
    $email = ($_POST['email']);
4

3 回答 3

3

使用准备好的语句、PDO 或 mysqli 我个人更喜欢 PDO,但两者都能胜任。

于 2013-01-22T07:43:34.103 回答
3

无论您做什么,请在此处阅读有关注射的信息。正如您在此站点上看到的,准备好的语句是要走的路,最好使用 PDO:

$stmt = $pdo->prepare('SELECT foo from db.bar WHERE foobar = :something;');
$stmt->execute(array(':something' => $_POST['something']));

无需依赖已弃用的功能,例如魔术引号,或者实际上mysql_*扩展中的任何内容;因为后者已被完全弃用,最终将一起从语言中删除(希望不久之后)。

如果您(或其他人)想知道为什么我认为 PDO 是首选:

  • 它支持多个驱动程序(MySQL,MSSQL,PostrgreSQL,...)完整列表在这里
  • 它的 OO API 更加与时俱进,同时mysqli_*也提供了过程 API。这被一些人认为是一个加分项,主要是那些不熟悉 OOP 的人,但你迟早要学习。
  • 就我个人而言,我的印象是 PDO 被更广泛地使用,并且由于 PHP 是一个开源产品,所以社区使用什么才是最重要的:它会被支持更长的时间,它会被更好地测试(和更多的人),如果你被困住了,还有更多的同行支持。
  • PDO emulates prepares, you can turn this off but that'll slow you down. Also, when emulating, PDO takes a closer look at the placeholders you're using. If, for example some wacky driver doesn't support named placeholders, you can still use them, but PDO will replace them with ?, or as the man pages put it "something more appropriate". All in all, PDO is quite clever :)
于 2013-01-22T07:47:02.340 回答
0

您列出的所有功能都没有连接到 SQL 注入 :)

  • stripslashes 只是为了从魔术引号中恢复
  • 魔术引号是一个错误功能,已从语言中删除
  • mysql_real_escape_string (你使用它的方式) - 只是魔术引号的手动副本

事实上,转义(通过魔术引号和 real_escape 完成)只是正确构建查询所需的真实格式的一小部分。一个人必须遵守一整套规则,而不仅仅是一个人“逃避一切,你就没事”。

尽管如此,逃跑本质上并没有错,只要它被用于它的位置,而不是作为保护你免受注射的魔杖。例如,PDO 一直在内部使用它。

请注意,“使用准备好的语句”也不是魔术棒,而是与“逃避一切”相同的口头禅——不够且不安全。

我之前发布的一些解释

你会建议这个功能没用吗?

我建议这个功能非常有害

类似问题的一些解释

有什么真正好的方法吗?

始终使用占位符向查询添加动态部分。它是安全的,使用起来非常方便。请注意,prepared statements 不是使用占位符的唯一方法,显然也不是最可靠的方法。

另外,请记住,在应用程序代码中使用原始 API 调用,无论是 PDO、mysqli 还是 mysql,都同样糟糕。必须使用抽象库来处理他们的 SQL,而不是原始 API 函数。

另外,请记住,大多数传播 PDO 的人从未将它用于真正的项目,或者至少其中一些是“好处”。他们实际上只是重复他们刚刚从其他人那里听到的相同文本;)

  • 话说,“多数据库支持”不是经常使用的东西,当然也不是仅仅改变DSN字符串那么简单。切换数据库是一项相当繁琐的任务,不同的API函数问题最小。
  • 或者说“关闭仿真会让你慢下来”的人从来没有真正尝试过:)
  • 或者,请说“PDO 非常聪明”的人创建一个简单的查询,其中包含从数组填充的 IN 子句;)
于 2013-01-22T07:39:32.207 回答