引言:为什么SQL注入在2025年仍然重要?
想象一下:你用PHP编写了一个很棒的网站,添加了反馈表单和商品搜索功能,但一周后你发现攻击者已经下载了整个客户数据库。这听起来像是2010年代的恐怖故事?不幸的是,根据OWASP的数据,SQL注入(SQL injection)至今仍位列Web应用程序最危险的三大漏洞之中。是的,在2025年,开发者仍然会犯这个错误。
Stack Overflow上关于"How can I prevent SQL injection in PHP?"的问题已经积累了数千次浏览和数百个回答。但问题不在于缺乏信息——问题在于许多人仍然使用过时的方法,比如mysql_*函数(早在PHP 7.0中就被移除了),或者天真地将mysqli_real_escape_string()视为万能药。
在本文中,我们将探讨2025年PHP中防范SQL注入的最佳实践。你将了解为什么预处理语句(prepared statements)是行业标准,如何正确使用PDO和mysqli,以及哪些陷阱仍在等待着经验不足的开发者。我们开始吧!
什么是SQL注入?简单解释
SQL注入是一种攻击,攻击者通过用户输入(表单、URL、Cookie)将恶意SQL代码“注入”到查询中。黑客不是简单地传递用户名,而是可能发送:' OR '1'='1——从而访问所有数据。
这是一个经典的易受攻击代码示例:
// 永远不要这样做!$username = $_POST['username'];$password = $_POST['password'];$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";$result = mysqli_query($conn, $query);如果攻击者在用户名字段中输入:admin' --,那么查询将变为:
SELECT * FROM users WHERE username = 'admin' --' AND password = '...'--之后的所有内容都是注释。身份验证通过。数据库被攻破。你好,数据泄露!
方法一(也是最重要的):预处理语句(Prepared Statements)
在2025年,这是唯一推荐的防止SQL注入的方法。预处理语句将SQL逻辑与数据分离。你首先告诉数据库:“这是一个查询模板,这里是变量的位置”,然后单独传递数据。数据库会自动转义所有需要的内容。
选项A:PDO(PHP数据对象)——现代标准
PDO支持12种不同的数据库管理系统(MySQL、PostgreSQL、SQLite等)。如果你在2025年编写新项目,请使用PDO。
<?php// 通过PDO连接数据库$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';$user = 'root';$password = '';
try { $pdo = new PDO($dsn, $user, $password); // 重要:出错时抛出异常 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);} catch (PDOException $e) { die('连接错误:' . $e->getMessage());}
// 使用命名占位符的预处理语句$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');$stmt->execute([ ':email' => $_POST['email'], ':status' => 'active']);$user = $stmt->fetch(PDO::FETCH_ASSOC);
// 使用位置占位符 (?) 的替代方案$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');$stmt->execute([$_POST['email'], 'active']);$user = $stmt->fetch();?>为什么这很安全?因为数据永远不会直接插入到SQL字符串中。即使传递了' OR '1'='1,PDO也会将其作为字符串字面量传递,数据库会将其视为字符串,而不是SQL命令的一部分。