在PHP中向数据库提交字符串时,我应该使用htmlspecialchars()处理非法字符还是使用正则表达式?

20

我正在处理一个表单,允许用户在要提交到数据库的字符串中使用非法/特殊字符。我想在字符串中转义/否定这些字符,并一直使用 htmlspecialchars() 。但是,是否有更好/更快的方法?


有两个阵营在讨论“查询中的非法字符”和“由于XSS攻击/ HTML而引起的非法字符”。我认为你说的是第一个,但你可能需要更清晰地说明一下。 - Wrikken
HTML不是SQL。使用HTML工具来避免SQL问题就像在阿拉伯文本上使用英语拼写检查器一样。 - Álvaro González
6个回答

30

数据库没有“非法”字符。无法存储某些字符的数据库是无意义的。有一些服务字符,比如引号,用于分隔字符串。这些字符应该被转义而不是消除。

向数据库发送查询时,您有两个选项:

  1. 通常的方式构建查询,使其看起来与可以在 SQL 控制台中运行的 SQL 查询完全相同。
    为此,人们应该理解 一整套规则,而不仅仅是“使用 mysql_real_escape_string”。
    这些规则包括:

    • 字符串应该被引号括起并进行转义。这就是转义的唯一含义:转义定界符!(以及一些其他字符-字符串终止字符和转义字符本身)。如果没有周围的引号,mysql_real_escape_string 就毫无用处。
    • 数字应该被显式地强制转换为其类型。虽然在数据方面,数字可以像字符串一样处理,但有一些数字,比如 LIMIT 子句参数,不能被转义,只能被强制转换。
  2. 将查询和数据分开发送。
    这是最优选的方法,因为它可以简化为只需“使用绑定”。所有字符串、数字和 LIMIT 参数都可以绑定-毫无担忧。
    使用此方法,您的带有占位符的查询将原样发送到数据库,并且绑定数据将在单独的数据包中发送,因此不会干扰查询。这就像 代码数据 分离一样。您将程序(查询本身)与数据分开发送。

但是!

上面所说的所有内容仅涵盖了查询的数据部分。
但有时我们必须使查询更加动态,添加运算符或标识符。
在这种情况下,每个动态参数都应在我们的脚本中被硬编码,并从该集合中选择。
例如,要进行动态排序:

$orders  = array("name","price","qty"); //field names
$key     = array_search($_GET['sort'],$orders)); // see if we have such a name
$orderby = $orders[$key]; //if not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; //value is safe

或者动态搜索:

$w     = array();
$where = '';

if (!empty($_GET['rooms']))     $w[]="rooms='".mesc($_GET['rooms'])."'";
if (!empty($_GET['space']))     $w[]="space='".mesc($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";

if (count($w)) $where="WHERE ".implode(' AND ',$w);
$query="select * from table $where";
在这个例子中,我们只向查询添加用户输入的数据,而不是字段名称,这些字段名称都是在脚本中硬编码的。对于绑定,算法也非常相似。
等等。

13

如果您将此数据提交到数据库,请查看数据库的转义函数。

例如,对于MySQL,有mysql_real_escape_string

这些转义函数可以处理可能存在恶意的任何字符,您仍然可以以相同的方式获取数据。

您也可以使用预处理语句来处理数据:

$dbPreparedStatement = $db->prepare('INSERT INTO table (htmlcontent) VALUES (?)');
$dbPreparedStatement->execute(array($yourHtmlData));

或者更加自我解释一些:

$dbPreparedStatement = $db->prepare('INSERT INTO table (htmlcontent) VALUES (:htmlcontent)');
$dbPreparedStatement->execute(array(':htmlcontent' => $yourHtmlData));

如果您想保存不同类型的数据,请使用 bindParam 来定义每种类型,例如可以通过以下方式定义整数:$db->bindParam(':userId', $userId, PDO::PARAM_INT);。示例:

$dbPreparedStatement = $db->prepare('INSERT INTO table (postId, htmlcontent) VALUES (:postid, :htmlcontent)');
$dbPreparedStatement->bindParam(':postid', $userId, PDO::PARAM_INT);
$dbPreparedStatement->bindParam(':htmlcontent', $yourHtmlData, PDO::PARAM_STR);
$dbPreparedStatement->execute();

如果您没有使用PHP数据对象(PDO),可以在PHP Data Objects了解更多信息,其中$db是您的PHP数据对象(PDO)。


这正是我正在寻找的。 - Brook Julias
6
更好的解决SQL注入问题的方法是使用参数化查询。这可以完全消除手动转义的需要。 - Matti Virkkunen
2
使用参数化查询。为什么要引导人们使用更容易受到注入攻击的旧技术呢? - webbiedave
1
不清楚问题在问什么。是SQL注入还是XSS? mysql_real_escape_string无法防止XSS,而htmlspecialchars无法防止SQL注入。如果我还有任何投票机会,我会-1这个mysql_escape_string函数: “自PHP 5.3.0版以来,此函数已被弃用。依赖此功能是不可取的。”并给提到参数化查询的人+1。 - Lotus Notes
@favo: 不是“足够”,而是他们“全部拥有”的。 @Byron: 有趣的是文档说从5.3.0开始过时mysql_escape_string已经过时多年了。请查看这个来自2004年的存档页面:http://web.archive.org/web/20041207044948/http://us2.php.net/mysql_escape_string - webbiedave
显示剩余4条评论

2

首先,当展示内容时,应该对其进行清洗,而不是在插入数据库之前。SQL注入则是另一个话题,可能与本文无关。

其次,如果您的用户不需要发布HTML,那么只需使用htmlspecialchars即可。它会处理HTML中的所有特殊字符。


哇,这正是我想在“新答案”出现时写的内容 ;) - Marian
那么使用JavaScript在输入时对文本进行清理处理? - Brook Julias
6
@Brook:什么?你怎么会想到那个?那完全不相关啊! - Matti Virkkunen
2
@Brook,永远不要相信来自客户端的任何东西。如果你正在使用客户端JavaScript,他们可以轻松地绕过它。当他们在客户端点击提交时,请随意进行验证,但不要相信已经通过验证。您需要在服务器上重新验证。 - TheJacobTaylor

1
我正在处理一个表格,用户可以在要提交到数据库的字符串中使用非法/特殊字符。 实际上,用户可以做得比这个更多。 我想对字符串中的这些字符进行转义/否定,一直在使用htmlspecialchars()。但是,我想知道是否有更好/更快的方法。
使用HTML净化器
HTML Purifier是一个用PHP编写的符合标准的HTML过滤库。 HTML Purifier不仅会使用经过彻底审核的、安全而允许的白名单删除所有恶意代码(更为常见的是XSS)。 你自己决定吧 :)

1
感谢提供HTML Purifier的链接。看起来它会非常有帮助。 - Brook Julias

0

这不是你想独自解决的问题。有一些库可以为您完成此操作,例如HTML Purifier


这绝对不是我想独自解决的问题。感谢提供链接,HTML Purifier看起来会特别有帮助。 - Brook Julias

0

您没有说明这些非法字符可能是什么,但您应该绝对使用数据库API提供的机制来转义数据。例如,如果您正在使用MySQL,请使用PDO参数化SQL语句。


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