如何对 PHP 中的用户输入进行消毒:常见错误与防护措施

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

“我只是转义了引号”:为什么你的代码仍然脆弱

\n\n

如果你曾经搜索过“How can I sanitize user input with PHP?”,你并不孤单。这个问题在 Stack Overflow 上获得了数十万次浏览。这并不奇怪:安全问题是 Web 开发中最热门的话题之一。

\n\n

但矛盾的是:大多数回答要么过时,要么危险。开发者仍然在使用类似 mysql_real_escape_string()(该函数已消失 10 年)或直接删除“危险”字符。剧透:这根本没用。

\n\n

在本文中,我们将分析 PHP 用户输入消毒的 5 大常见错误,这些错误会让你的网站漏洞百出,并展示如何一劳永逸地修复它们。

\n\n

错误 #1:盲目相信“消毒”是万能药

\n\n

最大的错误是认为存在一个万能函数可以“清理”所有数据。以下是新手常见的代码:

\n\n
<?php\n$name = $_POST['name'];\n$name = strip_tags($name);\n$name = addslashes($name);\n// 现在数据安全了吗?不!\n?>
\n\n

问题在于,不同的上下文需要不同的处理方式。验证(检查格式)和消毒(清理)不是一回事。此外,还有输出转义,这完全发生在另一个阶段。

\n\n

正确做法:明确在每个阶段对数据做什么。对于数据库是一种方式,对于 HTML 是另一种,对于 JSON 又是另一种。

\n\n

错误 #2:使用过时的函数(或错误地使用它们)

\n\n

让我们回顾一下 PHP 安全领域的“恐龙”:

\n\n
    \n
  • mysql_real_escape_string() — 已在 PHP 7.0 中移除。忘掉它。
  • \n
  • addslashes() — 无法防御所有 SQL 注入,尤其是在多字节编码下。
  • \n
  • strip_tags() — 删除标签,但不转义输出。XSS 仍可能通过属性发生。
  • \n
\n\n

一个至今仍可见的危险代码示例:

\n\n
<?php\n$id = $_GET['id'];\n$id = addslashes($id);\n$query = "SELECT * FROM users WHERE id = '$id'";\n// 如果 id = 1 OR 1=1 -- 你已被入侵\n?>
\n\n

正确做法:使用预处理语句(prepared statements)。这是防御 SQL 注入唯一可靠的方法。

\n\n
<?php\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");\n$stmt->execute(['id' => $_GET['id']]);\n?>
\n\n

看到了吗?对于 SQL 不需要任何消毒。只需使用参数。数据库自己会区分哪些是数据,哪些是命令。

\n\n

错误 #3:混淆验证与消毒

\n\n

许多人认为:“我会删除所有可疑字符,这样就能保证安全。”但这会破坏应用程序的逻辑。想象一下,用户输入了密码 P@ssw0rd!,而你删除了 @ 和 ! 字符。密码变成了 Pssw0rd——用户再也无法登录。

\n\n

验证——检查输入是否符合预期格式(电子邮件、数字、URL)。如果不符合,则拒绝。

\n\n

消毒——删除或修改不需要的字符,但仅在确实需要时进行(例如,对于文件名)。

\n\n

正确验证电子邮件的示例:

\n\n
<?php\n$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);\nif ($email === false) {\n    // 电子邮件无效——不保存,不输出\n    echo "请输入有效的电子邮件地址";\n    exit;\n}\n// 现在 $email 是用于任何目的的安全字符串\n?>
\n\n

黄金法则:如果你期望一个数字——请检查它是否是数字。如果是电子邮件——请检查格式。其他所有情况——拒绝。

\n\n

错误 #4:忽略输出上下文(XSS)

\n\n

你已经通过预处理语句将数据保存到数据库。太好了,没有 SQL 注入。但

博客

书籍推荐