引言:从历史到问题
\n\n如果您曾经用PHP编写过代码并与MySQL数据库打过交道,那么您很可能见过像mysql_connect()或mysql_query()这样的代码。大约10到15年前,这曾是事实上的标准。但时代在变,如今使用mysql_*函数,就像试图在新款MacBook上运行Windows 95一样。虽然可以,但何必呢?
在Stack Overflow上,“为什么我不应该在PHP中使用mysql_*函数?”这个问题获得了数千次浏览和数百次投票。这并非偶然:安全性和现代数据库处理方法对于任何开发者来说都至关重要。在本文中,我们将剖析为什么mysql_*不仅仅是“过时”,而是明显危险,并展示应该用什么来替代它们。
问题一:mysql_*已从PHP 7中完全移除
\n\n最明显的原因是:从PHP 5.5.0开始,mysql_*函数被标记为deprecated(已弃用),而在PHP 7.0中则被完全移除。如果您的服务器升级到PHP 7+,所有使用mysql_connect()、mysql_query()等函数的代码将直接停止工作。您将收到致命错误:
Fatal error: Call to undefined function mysql_connect()\n\n这不是“可能无法工作”,而是网站必然崩溃。因此,第一个也是最重要的原因是:兼容性。任何主机商迟早都会升级PHP,届时您的项目将陷入死区。
\n\n问题二:缺乏对SQL注入的防护
\n\n让我们看看一个典型的mysql_*代码:
<?php\n$connection = mysql_connect('localhost', 'user', 'password');\nmysql_select_db('my_database', $connection);\n\n$username = $_POST['username'];\n$password = $_POST['password'];\n\n$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";\n$result = mysql_query($query, $connection);\n\nif (mysql_num_rows($result) > 0) {\n echo "欢迎!";\n} else {\n echo "用户名或密码错误";\n}\n?>\n\n看起来无害?现在想象一下,攻击者在username字段中输入以下字符串:
admin' OR '1'='1\n\n查询变成了:
\n\nSELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '...'\n\n瞧——'1'='1'始终为真,因此查询将返回表中的第一条记录。攻击者无需密码即可登录系统。这是一个经典的SQL注入。mysql_*函数不提供任何内置的数据转义机制——您必须手动使用mysql_real_escape_string(),而该函数本身也已过时。
与将防护内置于架构中的现代方法相比(稍后详述)。
\n\n问题三:过时的架构与缺乏支持
\n\nmysql_*是一个过程式接口,不支持:
- \n
- 预处理语句(prepared statements)——防御注入的主要工具。 \n
- 面向对象风格——现代PHP中通常使用对象进行开发。 \n
- 多语句查询——无法在一次调用中执行多条SQL命令。 \n
- 事务——虽然可以模拟,但没有内置支持。 \n
- 存储过程——与之配合使用极其不便。 \n
此外,mysql_*扩展自2014年以来就未再更新。PHP开发者已停止对其维护。这意味着任何新的数据库功能、安全改进或优化——所有这些都与您无缘。
应该用什么替代mysql_*?
\n\nPHP有两个现代且受支持的