SQL注入攻击是指攻击者通过在应用程序的输入参数中插入恶意SQL代码片段,篡改原有SQL语句的执行逻辑,从而非法获取、修改或删除数据库中的数据。这种攻击方式成本低、危害大,是Web应用开发中需要重点防范的安全问题。

什么是SQL注入攻击
当应用程序直接将用户输入的内容拼接到SQL语句中执行时,就可能存在SQL注入风险。比如用户登录场景,如果后端代码直接将用户名和密码拼接到查询语句里,攻击者可以输入特殊字符改变查询逻辑,绕过密码验证直接登录系统。
典型的注入漏洞示例
以下是一个存在SQL注入风险的Java代码片段:
// 存在注入风险的代码
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
如果攻击者在用户名输入框中输入admin' --,拼接后的SQL会变成SELECT * FROM user WHERE username = 'admin' --' AND password = 'xxx',其中--是SQL的注释符,后面的密码验证条件会被忽略,攻击者无需正确密码就能以admin身份登录。
SQL注入防御实用技巧
1. 使用参数化查询(预编译语句)
参数化查询是最有效的防注入手段,它会将SQL语句的结构和参数分开处理,数据库会把参数当作纯数据处理,不会执行参数中的恶意SQL代码。不同语言的参数化查询实现方式略有不同:
Java示例(使用PreparedStatement)
// 安全的参数化查询代码
String username = request.getParameter("username");
String password = request.getParameter("password");
// ?是参数占位符
String sql = "SELECT * FROM user WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数,数据库会自动转义特殊字符
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
PHP示例(使用PDO)
<?php
$username = $_POST['username'];
$password = $_POST['password'];
// 使用PDO参数化查询
$sql = "SELECT * FROM user WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();
$result = $stmt->fetchAll();
?>
2. 严格的输入验证
对用户输入的内容进行类型、格式校验,只允许符合预期的内容进入SQL语句。比如手机号输入框只允许输入数字,邮箱输入框必须符合邮箱格式,长度也要做限制,避免超长输入带来的风险。
- 数字类型的参数直接转换为整数类型,非数字内容直接拒绝
- 字符串类型的参数校验长度,过滤掉单引号、分号、注释符等危险字符
- 使用白名单机制,只允许特定的输入值,比如状态字段只允许0和1两种取值
3. 遵循最小权限原则
应用程序连接数据库时使用的账号不要赋予过高权限,避免使用root、sa等管理员账号。比如普通查询功能只给查询权限,新增功能只给插入权限,即使发生注入攻击,攻击者能执行的操作也会受限,降低损失。
4. 避免动态拼接SQL语句
尽量不要使用字符串拼接的方式生成SQL语句,尤其是拼接用户输入的内容。如果必须使用动态SQL,要对拼接的内容做严格的转义处理,比如使用数据库提供的转义函数,对单引号、反斜杠等特殊字符进行转义。
5. 定期安全审计与测试
开发完成后可以使用SQL注入检测工具对接口进行扫描,也可以手动测试常见的注入 payload,及时发现潜在的漏洞。同时定期更新数据库版本和驱动,修复已知的安全漏洞。
常见误区提醒
很多开发者认为对用户输入做简单的单引号替换就能防注入,比如把单引号替换成两个单引号,这种方式很容易被绕过,比如攻击者可以使用宽字节注入等方式突破过滤。因此不要依赖简单的字符替换,优先使用参数化查询等成熟的防护方案。
另外,ORM框架虽然默认做了参数化处理,但如果开发者在ORM中手动拼接SQL语句,依然可能存在注入风险,使用ORM时也要注意避免直接拼接用户输入的内容。