Skip to content

MySQL SQL 注入防护 ​

攻击原理 ​

SQL 注入的基本概念 ​

SQL 注入是一种常见的 Web 应用安全漏洞,攻击者通过在用户输入中插入恶意 SQL 代码,使应用程序执行非预期的数据库操作。常见的攻击场景包括:

  • 登录绕过
  • 数据窃取
  • 数据篡改
  • 数据库服务器提权

攻击示例 ​

登录绕过攻击 ​

sql
-- 原始查询
SELECT * FROM users WHERE username = 'admin' AND password = 'password';

-- 注入攻击
SELECT * FROM users WHERE username = 'admin' -- ' AND password = 'anything';

数据窃取攻击 ​

sql
-- 原始查询
SELECT * FROM products WHERE id = '1';

-- 注入攻击
SELECT * FROM products WHERE id = '1' UNION SELECT username, password FROM users;

代码层面防护 ​

参数化查询 ​

使用预处理语句 ​

PHP 示例

php
// 不安全的做法
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

// 安全的做法
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);

Java 示例

java
// 不安全的做法
String username = request.getParameter("username");
String password = request.getParameter("password");
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";

// 安全的做法
PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();

输入验证 ​

类型验证 ​

  • 对数字类型的输入进行数值范围检查
  • 对日期类型的输入进行格式验证
  • 对字符串类型的输入进行长度限制

白名单过滤 ​

php
// 只允许特定字符
function validate_input($input) {
    return preg_match('/^[a-zA-Z0-9_]+$/', $input);
}

转义特殊字符 ​

使用内置转义函数 ​

PHP 示例

php
$username = mysqli_real_escape_string($connection, $_POST['username']);
$password = mysqli_real_escape_string($connection, $_POST['password']);
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

数据库层面防护 ​

最小权限原则 ​

用户权限设置 ​

sql
-- 创建应用专用用户
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'strong_password';

-- 只授予必要的权限
GRANT SELECT, INSERT, UPDATE ON app_db.* TO 'app_user'@'localhost';

-- 禁止危险操作
REVOKE ALTER, DROP, CREATE ON *.* FROM 'app_user'@'localhost';

存储过程使用 ​

优点 ​

  • 封装 SQL 逻辑,减少直接执行 SQL 的机会
  • 可以设置执行权限,限制用户操作
  • 提高性能,减少网络传输

示例 ​

sql
DELIMITER //

CREATE PROCEDURE get_user(IN user_id INT)
BEGIN
    SELECT * FROM users WHERE id = user_id;
END //

DELIMITER ;

-- 调用存储过程
CALL get_user(1);

数据库防火墙 ​

MySQL Enterprise Firewall ​

  • 基于语句模式的防火墙
  • 学习模式和保护模式
  • 防止未授权的 SQL 语句执行

第三方防火墙 ​

  • ProxySQL
  • MaxScale
  • 应用层 WAF

配置层面防护 ​

禁用危险功能 ​

ini
# my.cnf 配置
[mysqld]
# 禁用 LOAD DATA INFILE
local-infile=0

# 禁用符号链接
symbolic-links=0

# 禁用远程 root 登录
skip-networking=1

错误信息处理 ​

生产环境配置 ​

php
// 开发环境
error_reporting(E_ALL);
ini_set('display_errors', 1);

// 生产环境
error_reporting(0);
ini_set('display_errors', 0);
ini_set('log_errors', 1);

自定义错误页面 ​

  • 对用户显示通用错误信息
  • 详细错误信息只记录到日志
  • 避免在错误信息中泄露数据库结构

监控与审计 ​

审计日志配置 ​

ini
# my.cnf 配置
[mysqld]
# 启用审计日志
audit_log=ON
audit_log_format=JSON
audit_log_file=/var/log/mysql/audit.log

异常 SQL 检测 ​

  • 监控执行时间过长的 SQL 语句
  • 检测大量数据查询操作
  • 识别异常的表访问模式

安全扫描 ​

定期安全评估 ​

  • 使用专业的 SQL 注入扫描工具
  • 进行渗透测试
  • 代码审计

常见工具 ​

  • SQLmap
  • OWASP ZAP
  • Burp Suite

最佳实践 ​

开发规范 ​

  1. 强制使用参数化查询
  2. 实施输入验证
  3. 使用 ORM 框架
  4. 定期代码审计

部署规范 ​

  1. 遵循最小权限原则
  2. 定期更新 MySQL 版本
  3. 配置数据库防火墙
  4. 启用审计日志

应急响应 ​

  1. 建立安全事件响应流程
  2. 定期备份数据库
  3. 制定数据泄露应急预案
  4. 保持安全团队的技术培训

常见问题(FAQ) ​

Q1: 使用 ORM 框架是否可以完全防止 SQL 注入? ​

A1: ORM 框架可以大大减少 SQL 注入的风险,但不能完全防止。仍然需要注意:

  • 避免使用原生 SQL 拼接
  • 正确使用 ORM 的参数绑定功能
  • 对复杂查询进行安全审查

Q2: 存储过程是否比直接执行 SQL 更安全? ​

A2: 存储过程可以提高安全性,但也需要注意:

  • 避免在存储过程中使用动态 SQL
  • 正确处理存储过程的输入参数
  • 限制存储过程的执行权限

Q3: 如何检测已经发生的 SQL 注入攻击? ​

A3: 可以通过以下方式检测:

  • 分析数据库审计日志中的异常 SQL 语句
  • 监控服务器日志中的可疑访问模式
  • 使用入侵检测系统 (IDS) 进行实时监控
  • 定期检查数据库中的异常数据修改

Q4: 除了技术措施,还有哪些方法可以防止 SQL 注入? ​

A4: 还可以采取以下措施:

  • 对开发人员进行安全培训
  • 建立安全编码规范
  • 实施代码审查流程
  • 定期进行安全测试和评估
  • 建立安全事件响应机制