FILTER_SANITIZE_EMAIL 是否足够或还需要 mysql_real_escape_string 来避免 PHP/MySQL 中的安全问题?
Is FILTER_SANITIZE_EMAIL enough or is mysql_real_escape_string also needed to avoid security issues in PHP/MySQL?
我正在检查这样一个数据库的输入:
$email = $_POST['newsletter_email'];
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
然后:
$is_user = $db->count_rows('users', "WHERE email='" . $email . "'");
问题是,它够好吗?或者我也应该使用 mysql_real_escape_string
?
您是 "sanitizing" 而不是 "validating" 输入。仅仅因为您使用了 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. "'");
}
?>
这个问题有两个部分。
- 如何验证电子邮件地址。
- 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 的正确方法是使用参数化查询。也称为准备好的语句。
推荐阅读:
- SQL injection that gets around mysql_real_escape_string()
- https://kraft.im/2015/05/how-emoji-saved-your-sites-hide/
您的代码应如下所示:
$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 hacks。
重要
您必须正确使用准备好的语句(即永远不要将用户输入与传递给 prepare()
的查询字符串连接起来)以达到此保证的安全级别。
此外,在 PHP 中,某些驱动程序将默认 模拟 准备好的语句。来自 PHP manual:
PDO::ATTR_EMULATE_PREPARES Enables or disables emulation of prepared statements. Some drivers do not support native prepared statements or have limited support for them. Use this setting to force PDO to either always emulate prepared statements (if TRUE
), or to try to use native prepared statements (if FALSE
). It will always fall back to emulating the prepared statement if the driver cannot successfully prepare the current query. Requires bool.
模拟的准备好的语句不提供与真正的准备好的语句相同的安全保证(即 data/instruction separation)。为了安全起见,请禁用模拟准备。
$db = new PDO(/* etc */);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
我正在检查这样一个数据库的输入:
$email = $_POST['newsletter_email'];
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
然后:
$is_user = $db->count_rows('users', "WHERE email='" . $email . "'");
问题是,它够好吗?或者我也应该使用 mysql_real_escape_string
?
您是 "sanitizing" 而不是 "validating" 输入。仅仅因为您使用了 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. "'");
}
?>
这个问题有两个部分。
- 如何验证电子邮件地址。
- 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 的正确方法是使用参数化查询。也称为准备好的语句。
推荐阅读:
- SQL injection that gets around mysql_real_escape_string()
- https://kraft.im/2015/05/how-emoji-saved-your-sites-hide/
您的代码应如下所示:
$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 hacks。
重要
您必须正确使用准备好的语句(即永远不要将用户输入与传递给 prepare()
的查询字符串连接起来)以达到此保证的安全级别。
此外,在 PHP 中,某些驱动程序将默认 模拟 准备好的语句。来自 PHP manual:
PDO::ATTR_EMULATE_PREPARES Enables or disables emulation of prepared statements. Some drivers do not support native prepared statements or have limited support for them. Use this setting to force PDO to either always emulate prepared statements (if
TRUE
), or to try to use native prepared statements (ifFALSE
). It will always fall back to emulating the prepared statement if the driver cannot successfully prepare the current query. Requires bool.
模拟的准备好的语句不提供与真正的准备好的语句相同的安全保证(即 data/instruction separation)。为了安全起见,请禁用模拟准备。
$db = new PDO(/* etc */);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);