我过去曾遇到过mySQL注入攻击的问题,因此我使用PDO准备的语句编写了一个新的登录脚本。如果有人能关注它,让我知道它是否足够安全,可以在我的网站上使用,我将不胜感激。
代码如下:
if(isset($_POST['login'])&& !empty($_POST['username']) && !empty($_POST['password']))
{
$username=$_POST['username'];
$password=sha1($_POST['password']);
$sql = "SELECT * FROM admin WHERE username ='".$username."' AND password = '".$password."'";
$result = $PDOdbh->query($sql)->fetchAll();
$check = count($result);
if($check > '0') {
$_SESSION['loggedin'] = "1";
$_SESSION['username'] = "".$username."";
header("Location: index.php");
}
else {
$_SESSION['loggedin'] = "0";
$_SESSION['username'] = "";
header ("Location: login.php?error");
}
}
if(isset($_POST['login'])&& empty($_POST['username']) && empty($_POST['password']))
{
header ("Location: login.php?missing");
}
在管理员的index.php页面上,我调用了以下函数:
function checkloggedin() {
if($_SESSION['loggedin'] == "0" || $_SESSION['loggedin'] !== "1" || $_SESSION['username'] == "") {
header("Location: login.php");
exit;
}
}
为了回答您的问题,伙计,您的代码仍然容易受到SQL注入的攻击。它甚至没有使用事先准备好的语句。(目前还不清楚你是否理解什么是事先准备好的声明。)
因此,这里有一个准备好的语句示例:
$stmt = $PDOdbh->prepare("SELECT 1 FROM admin WHERE username = :p1 AND password = :p2");
$stmt->bindParam(':p1', $username);
$stmt->bindParam(':p2', $password);
if ($stmt->execute() ) {
while ($row = $stmt->fetch()) {
// we got a row back
}
}
请注意"prepare
"方法和"bindParam
"方法。我们将$stmt
称为"事先准备好的声明"。(这可能与调用名为"prepare
"的方法返回有关,但谁真的知道呢?)
(显然,这只是一个例子,应该检查prepare的实际返回,以验证该方法是否成功,并且没有抛出错误。)
考虑到您的新尝试,您似乎甚至不了解SQL注入漏洞实际上是什么,以及如何识别它
为了说明SQL注入是如何工作的,让我们举一个非常简单的例子,并考虑以下值:
$user = "a' OR 1=1 -- ";
$pass = "doodah";
$sql = "SELECT * FROM admin WHERE username = '".$user."' AND password = '".$pass."'";
$sql
的内容评估为
SELECT * FROM admin WHERE username ='a' OR 1=1 -- ' AND password = 'doodah'
当它被发送到数据库时,--
后面的所有内容都将被视为注释,因此这实际上相当于:
SELECT * FROM admin WHERE username ='a' OR 1=1
执行该语句时,如果管理表中至少有一行(并且管理表存在,并且我们对该表具有选择权限,并且用户名列存在于该表中,等等),则该语句将返回至少一行。
此外,考虑一个更熟悉的例子:
$username = "Robert'; DROP TABLE students; -- ";
"我们叫他小鲍比桌"http://xkcd.com/327/:
使用准备好的语句是减轻这种类型的SQL注入漏洞的一种方法。
根据准备好的语句(在问题顶部的示例中),发送到数据库的SQL文本(*)是:
SELECT 1 FROM admin WHERE username = :p1 AND password = :p2
这是一个常量字符串。为:p1
和:p2
提供的值(当执行语句时)只能解释为值。这些值的内容不可能被评估为SQL语法,例如关键字、标识符或分隔符。(仅在这个准备好的SQL语句的上下文中是这样。在其他语句(如INSERT)的情况下,分配给列的值可以由TRIGGER访问,并且可能有人在触发器中创建了漏洞。)
(*)在MySQL的情况下,这并不完全正确;与其他数据库一样,服务器实际上不接受准备好的语句。使用MySQL,客户端库处理准备好的声明,并对绑定占位符进行安全(正确转义)替换。
感谢您的评论(好吧,其中一些)
我已经修改了我的代码,请参阅下文。我还需要将mysql_real_eescape_string添加到$_POST值中吗?非常感谢。
if(isset($_POST['login'])&& !empty($_POST['username']) && !empty($_POST['password']))
{
$username=$_POST['username'];
$password=sha1($_POST['password']);
$stmt = $PDOdbh->prepare("SELECT * FROM admin WHERE username = :p1 AND password = :p2");
$stmt->bindParam(':p1', $username);
$stmt->bindParam(':p2', $password);
if ($stmt->execute() ) {
$row = $stmt->fetch();
if($row) { // Entry found in DB
$_SESSION['loggedin'] = "1";
$_SESSION['username'] = "".$username."";
header("Location: index.php");
}
else { // Entry not found in DB
$_SESSION['loggedin'] = "0";
$_SESSION['username'] = "";
header ("Location: login.php?error");
}
}
}
if(isset($_POST['login'])&& empty($_POST['username']) && empty($_POST['password']))
{
header ("Location: login.php?missing");
}