外观
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
最佳实践
开发规范
- 强制使用参数化查询
- 实施输入验证
- 使用 ORM 框架
- 定期代码审计
部署规范
- 遵循最小权限原则
- 定期更新 MySQL 版本
- 配置数据库防火墙
- 启用审计日志
应急响应
- 建立安全事件响应流程
- 定期备份数据库
- 制定数据泄露应急预案
- 保持安全团队的技术培训
常见问题(FAQ)
Q1: 使用 ORM 框架是否可以完全防止 SQL 注入?
A1: ORM 框架可以大大减少 SQL 注入的风险,但不能完全防止。仍然需要注意:
- 避免使用原生 SQL 拼接
- 正确使用 ORM 的参数绑定功能
- 对复杂查询进行安全审查
Q2: 存储过程是否比直接执行 SQL 更安全?
A2: 存储过程可以提高安全性,但也需要注意:
- 避免在存储过程中使用动态 SQL
- 正确处理存储过程的输入参数
- 限制存储过程的执行权限
Q3: 如何检测已经发生的 SQL 注入攻击?
A3: 可以通过以下方式检测:
- 分析数据库审计日志中的异常 SQL 语句
- 监控服务器日志中的可疑访问模式
- 使用入侵检测系统 (IDS) 进行实时监控
- 定期检查数据库中的异常数据修改
Q4: 除了技术措施,还有哪些方法可以防止 SQL 注入?
A4: 还可以采取以下措施:
- 对开发人员进行安全培训
- 建立安全编码规范
- 实施代码审查流程
- 定期进行安全测试和评估
- 建立安全事件响应机制
