-1

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

2 回答 2

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 表存在,并且我们对该表具有选择权限,并且该表中存在 username 列等),则该语句将返回至少一排。


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

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

“我们叫他小鲍比桌” http://xkcd.com/327/


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

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

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

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

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

于 2013-07-23T16:27:01.810 回答
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");
}
于 2013-07-24T08:15:13.800 回答