什么是SQL注入攻击?

SQL注入(SQL Injection) 是一种通过向应用程序的SQL查询中注入恶意SQL代码来操纵数据库的攻击方式。攻击者利用输入未被正确过滤的漏洞执行非预期的数据库操作,例如绕过身份验证、窃取数据、修改或删除数据,甚至执行数据库管理命令。


SQL注入的原理与示例

假设有一个登录表单,用户输入用户名和密码,后端通过如下SQL查询验证用户身份:

SELECT * FROM users WHERE username = '$username' AND password = '$password';

正常场景

用户输入:

  • 用户名:admin
  • 密码:12345

生成的SQL语句为:

SELECT * FROM users WHERE username = 'admin' AND password = '12345';
  • 如果存在匹配记录,则用户成功登录。

SQL注入攻击场景

攻击者输入:

用户名:' OR '1'='1
密码:' OR '1'='1

生成的SQL语句为:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '' OR '1'='1';

因为'1'='1'始终为真,这条SQL语句总是返回数据库中的所有用户记录。
攻击者成功绕过身份验证。


更多SQL注入的攻击形式

1. UNION查询

通过UNION合并查询获取其他表的数据。
攻击示例:

' UNION SELECT username, password FROM admin_table --

合成的SQL:

SELECT * FROM users WHERE username = '' UNION SELECT username, password FROM admin_table -- AND password = '';

2. 布尔盲注

通过修改查询条件判断返回页面的不同,逐步猜测数据库信息。
示例输入:

' AND 1=1 --
' AND 1=2 --

3. 时间盲注

利用SQL的SLEEP()函数判断查询是否被成功执行。
示例输入:

' OR IF(1=1, SLEEP(5), 0) --

如何防止SQL注入攻击?

1. 使用预编译语句(Prepared Statement)

预编译语句通过将SQL语句与用户输入分开,避免SQL代码被篡改。

PHP+MySQL 示例:

<?php
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute([
    'username' => $username,
    'password' => $password
]);

2. 使用ORM框架

使用ORM(如HibernateJPAMyBatis)避免直接拼接SQL语句:

// 使用JPA查询
User user = entityManager.createQuery(
    "SELECT u FROM User u WHERE u.username = :username AND u.password = :password", User.class)
    .setParameter("username", username)
    .setParameter("password", password)
    .getSingleResult();

3. 对用户输入进行严格验证

验证输入的格式、长度、字符集。
对特殊字符(如', ", --, ;)进行转义或拒绝。

4. 使用最低权限的数据库用户

数据库连接用户应只拥有应用程序所需的最低权限,避免高权限账户被滥用。

5. 禁用危险的SQL功能

禁用或限制数据库用户对xp_cmdshellexec等高风险功能的访问。

6. 开启SQL日志监控

实时监控SQL查询日志,检测异常模式或潜在的SQL注入攻击。

7. 使用Web应用防火墙(WAF)

部署如AWS WAF或Cloudflare等工具,自动检测并阻止常见的SQL注入攻击。

8. 定期进行安全测试

使用工具(如SQLMap)模拟SQL注入攻击,对系统进行安全性评估。


SQL注入攻击的防护总结

  • 核心思想是永远不要信任用户输入
  • 参数化查询是最有效的防护手段。
  • 配合输入验证、权限控制和安全监控,可以大幅降低SQL注入的风险。