Real_escape_string阻止INSERT语句使用MYSQL PHP

时间:2015-07-16 14:45:25

标签: php mysql

所以我使用MYSQL和PHP制作自己的博客脚本。 我把整个博客写进了一个数据库'工作得很完美,直到我意识到如果你试图写一个带有语音标记的博客,这将阻止INSERT语句起作用(显然 - 语音标记结束了SQL语句)。

所以我尝试使用real_escape_string,现在即使你排除引号,INSERT也不起作用。 我尝试使用:

sqlstate

为了找出问题,它返回" 42000" - 在google搜索一点之后,它指的是语法错误,因为在使用real_escape_string之前没有语法错误,所以没有多大意义。

另外,我现在收到此错误:

 Call to a member function close() on a non-object in /postarticle.php on line 37

它指的是ELSE语句中的close()调用。

请你帮忙吗?已经绕圈了一会儿。这是我的代码:

<?php
$host = 'CENSORED';
$user = 'CENSORED';
$pass = 'CENSORED';
$db = 'CENSORED';

$connection = new mysqli($host,$user,$pass,$db);

$_SESSION["article"] = $_POST["article"];
$date_of_blog = getdate(); 

$article = ($_SESSION["article"]);

$sql1 = "SELECT * FROM `Blogs`";
$res1 = $connection->query($sql1);
$newrows = $res1->num_rows + 1;

$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES ('$newrows','$article','$date_of_blog')";
$sql2 = $connection->real_escape_string($sql2);
$res2 = $connection->query($sql2);

if ($res2->num_rows == $newrows)
{
    $res->close();
    $connection->close();
    header( 'Location: adminpanel.php' );

}
else
{

echo ($connection->sqlstate);
$connection->close();
$res->close();
}

exit();


?>

另外,在旁注中,我所获得的getdate()调用从未奏效。在数据库中,每篇博文都出现在:     0000:00:00 00:00:00

编辑: 问题现在解决了。找到下面的功能代码:

<?php
$host = 'CENSORED';
$user = 'CENSORED';
$pass = 'CENSORED';
$db = 'CENSORED';


$connection = new mysqli($host,$user,$pass,$db);

$_SESSION["article"] = $_POST["article"];

$article = ($_SESSION["article"]);

$article = $connection->real_escape_string($article);

$sql1 = "SELECT * FROM `Blogs`";
$res1 = $connection->query($sql1);
$newrows = $res1->num_rows + 1;

$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES (\"$newrows\",\"$article\",CURDATE())";
$res2 = $connection->query($sql2);

if ($res2 != false)
{
    header( 'Location: adminpanel.php' );
}
else
{   
    echo ($connection->sqlstate);
}
$connection->close();
$res->close();


exit();


?>

我很抱歉,如果这些问题是基本的,会让周围的专业人士感到恼火;我已经尝试遵循这些指导原则并且已经谷歌搜索了一段时间等等。我还没有找到任何与我的问题相符的解决方案。 谢谢你的时间。

2 个答案:

答案 0 :(得分:2)

最初发布的代码存在许多问题。主要是,您最初确定的两个问题的原因是滥用mysqli::real_escape_string()。需要在代码中出现的每个变量上调用它。因此,不要在整个语句中调用它,而是必须多次调用多个变量,如:

$article = $connection->real_escape_string($connection);

由于引用错误导致查询失败(由real_escape_string()引起)是错误消息调用close()的原因。

正如在评论中确定的那样,您正在使用num_rows + 1来验证是否已根据返回的先前行数插入了一个新行。由于一些原因,这是有问题的。主要是,它暴露了竞争条件,其中可以一次从两个会话插入一行,并且一个或两个将失败,因为$newrows的期望值不匹配。实际上BlogID应该是数据库中的auto_increment列。这消除了围绕它的任何逻辑的需要。您甚至不需要将其包含在INSERT中,因为它会自动递增。

这也完全消除了对第一个SELECT语句的需要。

将MySQL的本地NOW()函数替换为日期值,您可以将语句简化为:

INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES ('$article', NOW())

要测试插入的成功或失败,您只需要验证其变量不是false

将这些组合在一起,您的代码可以简化为:

if (!isset($_POST['article'])) {
   // exit or handle an empty post somehow...
}
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];

// Escape $article for later use
$article = $connection->real_escape_string($_SESSION["article"]);

// Only an INSERT is needed. $article is already escaped
$sql = "INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES ('$article', NOW())";
// Run the query
$res = $connection->query($sql);

// Test for failure by checking for a false value
if ($res) {
  // The connection & resource closure can be omitted
  // PHP will handle that automatically and implicitly.
  header( 'Location: adminpanel.php' );
  // Explictly exit as good practice after redirection
  exit();
}
else {
  // The INSERT failed. Check the error message
  echo $connection->error;
}

这应该使您当前的代码正常运行。但是,由于您正在学习这一点,因此现在是开始学习在MySQLi中通过prepare()/bind_param()/execute()使用预准备语句的绝佳时机。这是防止SQL注入的推荐最佳实践,尽管使用real_escape_string()只要您正确使用它并且永远不会忘记就可以工作。

有关示例,请参阅How can I prevent SQL injection in PHP

但它看起来像:

// connection already established, etc...
// Prepare the statement using a ? placeholder for article
$stmt = $connection->prepare("INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES (?, NOW())");
if ($stmt) {
  // bind in the variable and execute
  // Note that real_escape_string() is not needed when using
  // the ? placeholder for article
  $stmt->bind_param('s', $_SESSION['article']);
  $stmt->execute();

  // Redirect
  header( 'Location: adminpanel.php' );
  exit();
}
else {
  echo $connection->error;
}

答案 1 :(得分:1)

您需要将real_escape_string函数应用于变量而不是整个SQL字符串。

$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES ('".$connection->real_escape_string($newrows)."','".$connection->real_escape_string($article)."','".$connection->real_escape_string($date_of_blog)."')";

目的是删除MySQL可能被误解为查询函数的任何内容,但是您明显希望将查询的某些部分解释为这样。