谁能告诉我我的登录脚本是否安全?

时间:2013-07-23 15:53:16

标签: php mysql session login pdo

我过去曾遇到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");
}

在admin中的index.php页面上,我调用了以下函数:

function checkloggedin() {
if($_SESSION['loggedin'] == "0" || $_SESSION['loggedin'] !== "1" || $_SESSION['username'] == "") {
header("Location: login.php");
exit;
}
}

2 个答案:

答案 0 :(得分:4)

要回答你的问题,老兄,你的代码仍然容易受到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

当执行该语句时,如果admin表中至少有一行(并且admin表存在,并且我们对表有select特权,并且表中存在username列,等等),那么声明将至少返回一行。


此外,请考虑一个更为熟悉的例子:

$username = "Robert'; DROP TABLE students; -- ";

"Little Bobby Tables we call him" http://xkcd.com/327/


使用预准备语句是缓解此类SQL注入漏洞的一种方法。

从准备好的语句(在问题顶部的示例中),发送到数据库(*)的SQL文本是:

SELECT 1 FROM admin WHERE username = :p1 AND password = :p2

这是一个常量字符串。并且为:p1:p2提供的值(执行语句时)只能被解释为值。这些值的内容不可能被评估为SQL语法,例如关键字,标识符或分隔符。 (在这个准备好的SQL语句的上下文中也是如此。对于其他语句,如INSERT,分配给列的值可以由TRIGGER访问,并且有人可能在触发器中创建了漏洞。)

(*)就MySQL而言,这并不完全正确;服务器实际上并不接受预准备语句,就像其他数据库一样。使用MySQL,客户端库处理准备好的语句,并对绑定占位符进行安全(正确转义)替换。

答案 1 :(得分:0)

感谢您的评论(其中一些)

我修改了我的代码,见下文。我还需要将mysql_real_escape_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");
}