mysql_real_escape_string是否容易受到无效UTF-8字符的攻击,例如过长的UTF-8或格式不正确的UTF-8序列?

12

假设我已经按照以下方式设置了我的数据库以使用UTF-8(在MySQL中使用完整的4MB版本)

mysql_query("SET CHARACTER SET utf8mb4");
mysql_query("SET NAMES utf8mb4");

在将字符串放入 SQL 之前,我使用 mysql_real_escape_string 来转义不需要的字符(注意 - 我不是在寻求切换到 PDO 的建议,我想确定 mysql_real_escape_string 是否安全防止过长的 utf8 等问题)。

$input = mysql_real_escape_string($_POST['field']);
$sql = "SELECT * FROM `table` WHERE `header`='$input'";

在执行mysql_real_escape_string之前,我需要对$_POST['field']进行任何验证吗?例如,检查字符串是否有效的UTF-8编码,长度不超过限制,并且不包含无效字符序列等。还是直接使用mysql_real_escape_string就足够了?


一定要看 https://dev59.com/Xmcs5IYBdhLWcg3wmlWk#12720360 - deceze
谢谢deceze,我已经点赞了那个答案。你对这个链接有什么看法:http://sirdarckcat.blogspot.co.uk/2009/10/couple-of-unicode-issues-on-php-and.html(我知道那里没有使用mysql_real_escape_string,但那是我了解UTF-8过长和格式不正确序列的潜在危险的地方)。是否有在线资源可以确认mysql_real_escape_string正确处理这些UTF-8情况? - Hard worker
我认为转义应该发生在字符代表什么之前。例如,如果转义,您可以将任意随机二进制数据插入查询中;因此,在字符串文字中究竟有哪些字符并不重要。不过,对于这个话题,我不能给出明确的支持或反对。 - deceze
1
@deceze 我同意。我也认为您只需要转义单引号,因为这是唯一可能破坏SQL语法的字符,对吗?或者在UTF字符集中有其他“撇号”具有不同的代码吗?在这种情况下,MySQL不应该接受它,并且应该仅接受ASCII撇号,对吗?我无法想象发生什么事情会使用除ASCII撇号以外的其他字符来破坏SQL语法(在其他情况下也是双引号,但不适用于OP's用法)。 - Tomas
2个回答

2
在我的回答之前,先公布一条公共服务声明。您仍在使用mysql_query。即使您不想使用PDO,最终您也必须至少升级到mysqli。所有的mysql_函数都已过时(请参见上面链接中的大红色警告框),并且很可能会在PHP 5.6中被删除。这很重要,因为建议在您的情况下使用PDO的主要原因是prepared statements,而mysqli也可以执行此操作。准备好的语句比转义更不容易受到注入攻击,但需要更多的查询(小型性能损失)来完成。
至于UTF8,我建议使用mb_check_encoding来确保字符串至少是有效的UTF8,然后再尝试将其插入。
最后,这个答案提供以下智慧之言:
引起麻烦的另一种使用mysql_real_escape_string的方法是使用错误的方法设置数据库连接编码。你应该这样做:
mysql_set_charset('utf8', $link);
你也可以这样做:
mysql_query("SET NAMES 'utf8'", $link);
问题在于后者绕过了mysql_ API,它仍然认为你是在使用latin1(或其他东西)与数据库交互。现在使用mysql_real_escape_string时,它将假设错误的字符编码,并以不同于数据库稍后解释它们的方式转义字符串。通过运行SET NAMES查询,您已经在mysql_客户端API如何处理字符串和数据库如何解释这些字符串之间创建了一个分歧。在某些多字节字符串情况下,这可以用于注入攻击。

1
所有的输入验证和防SQL注入都受到了许多误解。实际上,所有这些都归结为一件事:确保正确的SQL查询语法。 如果您能确保任何输入数据的正确SQL语法,则您是安全的,根本不需要阅读或学习有关验证或SQL注入的任何内容。因为只有在允许不正确的SQL语法的情况下才可能存在所有这些漏洞。 要确保在您的情况下正确的SQL查询语法,您必须确保您的$input以正确的方式在查询中进行了转义。查看PHP文档:http://php.net/mysql_real_escape_string: 所以,mysql_real_escape_string必须正确通知您的字符集,以便能够正确转义。因此,您应该执行以下操作,而不是mysql_query("SET NAMES utf8mb4");
mysql_set_charset("utf8mb4");

3
如果MySQL的字符集仅为UTF-8,则即使使用mysql_real_escape_string,无效或棘手的UTF-8序列也可以截断SQL查询。因此,我想问一下,如果使用utf8mb4字符集,这种情况是否仍然存在。并且一般而言,我也想问一下mysql_real_escape_string对于utf8mb4是不是万无一失的...因为对于utf8字符集来说,它并不是,除非您先验证数据是有效的3MB最大UTF。 - Hard worker
@Hardworker:“众所周知,如果mysql的字符集仅为utf8,则即使使用mysql_real_escape_string,无效或棘手的utf-8序列也可能截断sql查询。” - 你确定你在这里说的是真的吗? 你能引用一些来源吗?如果这是真的,那将是一个很大的问题,在PHP世界中会引起很多关注,并几乎肯定会在PHP文档中提到!他们只警告你设置正确的字符集。所以我想他们会转义你所说的特殊utf-8序列。 - Tomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接