在mysqli中预防SQL注入
在mysqli中预防SQL注入
我之前对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']); 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'], '无效的登录详细信息'); $res['success'] = false; } }else{ $res['success'] = false; } echo json_encode($res); }
clean函数的工作不如预期,因为如果我输入用户名和密码正确,sql查询会返回false。所以在mysqli中似乎是无效的。我查看了这个链接PHP MySQLI Prevent SQL Injection,得知我们需要准备查询。我看到有一个示例,但我不知道如何准备/绑定如果我要使用两个或更多的表单数据。感谢您的时间。
更新后的代码
$result = $mysqli->prepare("SELECT uid FROM users where email=:email and password = :password"); $result->execute([ ':email' => $email, ':password' => $password ]); if($result->num_rows == 1){ $res['success'] = true; } else{ array_push($res['errors'], '无效的登录详细信息'); $res['success'] = false; }
防止SQL注入的原因是由于在数据库查询操作中,没有对用户输入的参数进行正确的过滤和处理,导致恶意用户可以通过在输入中插入SQL语句来执行恶意操作,如删除数据库、修改数据等。为了解决这个问题,可以使用mysqli的bind_param()函数来绑定参数,并指定参数的类型。
下面是一个示例代码,展示了如何使用bind_param()函数来防止SQL注入:
$stmt = $mysqli->prepare("SELECT uid FROM users where email= ? and password = ?"); $stmt->bind_param('ss', $email, $password); /* 执行预处理语句 */ $stmt->execute();
在上述代码中,使用prepare()函数将SQL语句进行预处理,然后使用bind_param()函数来绑定参数。'ss'表示参数的类型为字符串,$email和$password是用户输入的变量。通过这种方式,用户输入的参数会被正确地转义和处理,从而防止SQL注入攻击。
除了字符串类型的参数,还可以使用其他类型的变量。下面是一些可用的参数类型:
- i:整数类型
- d:浮点数类型
- s:字符串类型
- b:二进制数据类型
通过使用bind_param()函数,可以确保用户输入的参数在执行数据库查询操作时得到正确的处理,从而防止SQL注入攻击。
在这段对话中,出现了一个关于如何防止SQL注入的问题。问题的原因是使用了错误的函数(mysql_real_escape_string)和语法(直接在查询语句中插入变量),导致代码容易受到SQL注入攻击。解决方法是使用预处理语句(prepared statement)来绑定变量,从而避免了SQL注入的风险。
在原始代码中,使用了以下语句进行查询:
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
这段代码容易受到SQL注入攻击,因为直接将变量插入到查询语句中。
解决方法是使用预处理语句来绑定变量。以下是修改后的代码示例:
<?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(); //发布时删除 } if($stmt->num_rows){ $res['success'] = true; } ?>
这段代码使用了预处理语句来绑定变量。首先,使用prepare函数创建一个查询模板,其中用占位符(:email和:password)代替了变量。然后,使用execute函数执行查询,将变量与占位符进行绑定。这样可以确保输入的变量不会被当做SQL代码执行,从而防止了SQL注入攻击。
在对话中,还提到了使用mysql_real_escape_string函数和clean函数来防止SQL注入攻击的方法。然而,这些方法并不推荐使用,因为它们可能存在漏洞,而且不如预处理语句安全和方便。
最后,对话中还提到了使用PDO替代MySQLi的建议。因为PDO支持更多的驱动程序,所以更加灵活和强大。
随着评论中已经提到的,你需要在API的选择上保持一致。在这里,你开始使用了mysqli_*,所以我会继续使用它。你的代码中有一些mysql_*和PDO,使用PDO可能是一个不错的选择,但是如果你的服务器支持mysqli_*,使用它也没有问题。参考并自行决定(只需避免使用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']); // 绑定变量到占位符 $stmt->execute(); // 执行查询 $stmt->bind_result($userID, $password); // 将选定的列设置为变量 $stmt->fetch(); // ...并获取它 if ($stmt->num_rows) { if (password_verify($_POST['password'], $password)) { // 密码正确且与邮箱匹配! } else { // 密码错误... } } else { // 账户名不存在 } }
这只是一个基本示例,但它可以帮助你入门。永远不要相信用户的输入,使用预处理语句。