防止mysqli中的sql注入


prevent sql injection in mysqli

我对mysqli很陌生,之前我在mysql中编写查询,但mysqli更高级,所以我是第一次使用它。下面是我的php代码。

    function clean($str) {
        $str = @trim($str);
        if(get_magic_quotes_gpc()) {
            $str = stripslashes($str);
        }
        return mysql_real_escape_string($str);
    }
        $email = clean($_POST['email']);
        $password = clean($_POST['password']);
        //$password =md5($password);

    if(empty($res['errors'])) {
        $result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
        if($result->num_rows == 1){
            $res['success'] = true;
        }
        else{
            array_push($res['errors'], 'Invalid login details');
            $res['success'] = false;
        }
    }else{
        $res['success'] = false;        
    }
    echo json_encode($res);
}

clean函数不能按预期工作,因为如果我输入的用户名和密码正确,sql查询将返回false。所以,这在mysqli的情况下似乎是无效的。

我检查了这个链接PHP MySQLI防止SQL注入,并知道我们必须准备查询。

我可以看到有一个例子,但如果我必须使用两个或多个表单数据,我无法理解如何准备/绑定。

谢谢你抽出时间。

更新的代码

$result = $mysqli->prepare("SELECT uid FROM users where email=:email and password = :password");
        $result->execute([
':email' => $email,
         ':password' => $password]);
        //$result->execute();
        if($result->num_rows == 1){
        //if(mysqli_num_rows($result) === 1) {
            $res['success'] = true;
        }
        else{
            array_push($res['errors'], 'Invalid login details');
            $res['success'] = false;
        }

如注释中所述,您需要与您的API选择保持一致。PHP中不能混合使用API。

你是从mysqli_*开始的,所以我会继续。您有一些mysql_*和PDO,在mysqli_*上使用PDO可能不是一个坏主意,但如果您的服务器支持mysqli_*,那么使用它也没有什么问题。请参阅选择API并自行决定(只需远离mysql_*,它已经过时)。

使用mysqli_*,您可以像这样连接到数据库(您没有显示您的连接)。

$mysqli = new mysqli("host", "username", "password", "database");
if ($mysqli->connect_errno) {
    echo "Failed to connect to MySQL: (".$mysqli->connect_errno.") ".$mysqli->connect_error;
}
$mysqli->set_charset("utf8");

至于防止SQL自注入,您只需要使用准备好的语句。如果有一些值你不想坐在桌子上,你仍然可以清理或净化你的数据,但这是另一种讨论。

您还需要知道您的密码是否在数据库中进行了哈希处理它们确实应该是,如果您使用的是PHP5.5及以上版本,则应该使用password_hash($password, $algorithm)password_verify($password, $hash)(如果不是,请查看password_compat)。

你也需要与你的哈希保持一致,你不能用md5插入它,然后选择它而不使用哈希。一切都需要一样。因为如果您选择一个md5散列,并将其与未处理的字符串进行比较,则它们会有所不同,查询就会失败。

我向您展示了一个使用password_verify()的示例,这意味着存储在数据库中的密码也需要与password_hash()一起存储(否则查询将失败)。

if ($stmt = $mysqli->prepare("SELECT uid, password FROM users where email=?")) {
    $stmt->bind_param("s", $_POST['email']);           // Bind variable to the placeholder
    $stmt->execute();                                 // Execute query
    $stmt->bind_result($userID, $password);         // Set the selected columns into the variables
    $stmt->fetch();                                   // ...and fetch it
    if ($stmt->num_rows) {
        if (password_verify($_POST['password'], $password)) {
            // Password was correct and matched the email!
        } else {
            // Password was incorrect...
        }
    } else {
        // Accountname not found
    } 
}

这只是一个基本的例子,但它会让你开始永远不要相信用户的输入,使用事先准备好的语句。

您可以绑定更多变量,如下所示:

$stmt = $mysqli->prepare("SELECT uid FROM users where email= ? and password = ?");
$stmt->bind_param('ss', $email, $password);
/* execute prepared statement */
$stmt->execute();

正如您所看到的,您可以扩展bind_param()函数。您还可以添加不同类型的变量:

i   corresponding variable has type integer
d   corresponding variable has type double
s   corresponding variable has type string
b   corresponding variable is a blob and will be sent in packets

发件人:http://php.net/manual/en/mysqli-stmt.bind-param.php

首先,我建议您学习PDO而不是MySQLi,因为它支持更多的驱动程序。

第二,您可以使用mysql_real_sescape_string,正如您可能看到的,这是一个mysql函数,而不是MySQLi函数。

所以你有:

$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");

你应该做一些类似的事情:

<?php
$stmt = $dbConnection->prepare("SELECT uid FROM users where email = :email AND password = :password");
try{
$stmt->execute([
    ':email' => $email,
    ':password' => $password
]);
}
catch(Exception $e){
    echo $e->getMessage(); //Remove when putting online
}
if($stmt->num_rows){
    $res['success'] = true;
}
?>

您目前正在将MySQL API/函数与mysql_real_escape_string()num_rows和PDO绑定方法where email=:email and password = :password混合,这似乎是从您的问题的另一个答案中获得的。

  • 这些不同的功能不会混合在一起

从连接到查询都必须使用相同的。

  • 咨询:我可以在PHP中混合MySQL API吗

看起来您想要设置一个登录脚本。我建议你使用以下内容,并从ircmaxell的一个答案中提取:

从https://stackoverflow.com/a/29778421/

只要使用图书馆。认真地它们的存在是有原因的。

  • PHP 5.5+:使用password_hash()
  • PHP 5.3.7+:使用password-compat(上面的兼容包)
  • 所有其他:使用phpass

不要自己做。如果你在创造自己的盐,你做得不对。你应该使用一个为你处理这个问题的库。

$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);

登录时:

$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
    if (password_verify($_POST['password'], $users[0]->password) {
        // valid login
    } else {
        // invalid password
    }
} else {
    // invalid username
}

它更安全,并且使用了一种安全的密码哈希方法,而不是你似乎想要使用的MD5 $password =md5($password);,并且现在不再被认为是安全的。

参考文献:

  • PDO连接http://php.net/manual/en/pdo.connections.php
  • PDO错误处理http://php.net/manual/en/pdo.error-handling.php
  • 要检查用户是否存在,您可以看到我的一个答案https://stackoverflow.com/a/22253579/1415724
  • http://php.net/manual/en/mysqli.error.php
  • http://php.net/manual/en/function.error-reporting.php

旁注:如果你真的走了这条路,记得阅读手册,你的密码栏足够长,可以容纳哈希。最小长度为60,但他们建议为255。

还不清楚HTML表单是否具有POST数组的名称属性,因此请确保表单使用POST方法。

  • http://php.net/manual/en/tutorial.forms.php

我相信我已经给了你足够的信息来开始。


你不能做的是,将上面的代码与你现在的代码一起使用,并简单地进行修补。你需要重新开始。


错误报告添加到文件顶部,这将有助于查找错误。

<?php 
error_reporting(E_ALL);
ini_set('display_errors', 1);
// rest of your code

旁注:显示错误只能在暂存中进行,而不能在生产中进行。