【问题标题】:Is FILTER_SANITIZE_EMAIL enough or is mysql_real_escape_string also needed to avoid security issues in PHP/MySQL?FILTER_SANITIZE_EMAIL 是否足够,或者是否还需要 mysql_real_escape_string 来避免 PHP/MySQL 中的安全问题?
【发布时间】:2015-05-25 18:01:54
【问题描述】:

我正在根据这样的数据库检查输入:

$email = $_POST['newsletter_email'];
$email = filter_var($email, FILTER_SANITIZE_EMAIL); 

然后:

$is_user = $db->count_rows('users', "WHERE email='" . $email . "'");

问题是,它是否足够好?还是我也应该使用mysql_real_escape_string

【问题讨论】:

  • 你真的应该使用PDOMySQLi
  • FILTER_SANITIZE_EMAILphp.net/manual/en/filter.filters.sanitize.php 留下单引号,所以它是不够的。 Remove all characters except letters, digits and !#$%&'*+-/=?^_`{|}~@.[] 你应该使用带有 PDO 或 mysqli_ 的参数化查询。
  • 在安全方面为什么要以“足够好”为目标?
  • real_escape_string() != security(),它用于转义用于描述值的字符(引号)。因此,它用于进行有效查询,not 出于安全目的。混淆之处在于,没有正确处理数据,当您将其添加到查询的字符串中时,您将引入一个可被利用来运行任意查询/代码的问题。使用准备好的语句(PDO/MYSQLI)没有这个问题,因为参数是单独交给 MySQL 的,它知道如何处理插入它们以进行有效的查询。

标签: php mysql security sql-injection


【解决方案1】:

这个问题有两个部分。

  1. 如何验证电子邮件地址。
  2. How to prevent SQL Injection

输入验证

$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if ($email !== false) {
    // Well, you've got yourself a valid email address!
}

防止 SQL 注入

不要使用mysql_real_escape_string()prevent SQL Injection 的正确方法是使用参数化查询。也称为准备好的语句。

推荐阅读:

您的代码应如下所示:

$stmt = $db->prepare('SELECT count(userid) FROM users WHERE email = ?');
if ($stmt->exec([ $email ])) {
    $is_user = $stmt->fetchColumn(0);
}

如果这对您来说太麻烦,您可以加载 EasyDB 库并让您的查询看起来像这样:

$is_user = $db->cell('SELECT count(userid) FROM users WHERE email = ?', $email);

请停止使用输入转义并采用准备好的语句。它们在单独的数据包中发送查询和参数,这意味着即使使用 Unicode hack,也无法更改查询结构。

重要

您必须正确使用准备好的语句(即永远不要将用户输入与传递给 prepare() 的查询字符串连接起来)以实现这种有保证的安全级别。

此外,在 PHP 中,一些驱动程序会默认模拟准备好的语句。来自PHP manual

PDO::ATTR_EMULATE_PREPARES 启用或禁用准备语句的模拟。一些驱动程序不支持本机准备语句或对它们的支持有限。使用此设置可强制 PDO 始终模拟预准备语句(如果 TRUE),或尝试使用本机预准备语句(如果 FALSE)。如果驱动程序无法成功准备当前查询,它将始终回退到模拟准备好的语句。需要布尔值。

模拟的预准备语句不提供与真正的预准备语句相同的安全保证(即data/instruction separation)。为了安全起见,请禁用模拟准备。

$db = new PDO(/* etc */);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

【讨论】:

  • 我不相信推荐参数化查询而不是转义的有力。两者都可能被错误地使用,并且都容易受到您在特定配置下链接到的攻击(模糊字符集,模拟准备)。参数化语句可能是一个更好的习惯,但它们只是逃避的灵丹妙药。
  • 它们被证明是安全的:它们将数据与指令分开。是的,PHP 用仿真做了愚蠢的事情。把它关掉。
  • 它们被证明是安全的如果使用正确(并且配置正确,您在回答中没有提到)。完全有可能不安全地构建 SQL 语句,然后将其传递给准备函数;例如,当从查询字符串中获取列名进行排序时(参数化和标准转义函数都无济于事)。给人的印象是,一旦开始使用准备好的查询,就再也不需要考虑 SQL 注入了。
【解决方案2】:

您是在“清理”而不是“验证”输入。仅仅因为您使用了FILTER_SANITIZE_EMAIL,并不意味着输入的是电子邮件地址。所以是的,在你的例子中mysql_real_escape_string() 将是一个明智的选择。

<?php

$email = filter_var($_POST['newsletter_email'], FILTER_SANITIZE_EMAIL);

// Validate the post data as an email address.
IF (filter_var($email, FILTER_VALIDATE_EMAIL)) {
  $cln_email = mysql_real_escape_string($email);
  $is_user = $db->count_rows('users', "WHERE email='" . $cln_email. "'");
}
?>

【讨论】:

  • 如果电子邮件是john'badsql@site.com,这里会发生什么?
  • 请不要鼓励使用mysql_real_escape_string()
  • @Scott,这不是促销也不是鼓励。只需回答所提出的问题。
  • 我明确指的是“mysql_real_escape_string() 将是一个明智的选择”。 :)
  • > 所以是的,在您的示例中 mysql_real_escape_string() 将是一个明智的选择。直接来自他的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-05
相关资源
最近更新 更多