适用场景
本文适用于Web开发工程师、安全运维人员以及CTF参赛者,帮助你在实际项目中构建SQL注入防御体系。无论是自研Web应用还是二次开发CMS系统,本文提供的参数化查询与WAF规则配置方案均可直接落地。核心关键词:SQL注入防御、参数化查询、WAF规则配置。
前置条件
- Linux服务器(CentOS 7+ / Ubuntu 20.04+),具备root或sudo权限
- 已部署Nginx + PHP(或任意后端语言)的Web环境
- 已安装ModSecurity(Nginx WAF模块)或OpenResty
- 熟悉基本的SQL语法和正则表达式
原理说明
SQL注入攻击的本质是攻击者将恶意SQL代码拼接到用户输入中,利用后端未做参数校验的数据库查询语句执行非授权操作。完整的防御需要同时覆盖三个层面:
- 代码层:使用参数化查询(Prepared Statement)彻底杜绝拼接
- 输入层:对用户输入做严格的白名单校验和特殊字符过滤
- 网络层:部署WAF规则拦截异常请求
操作步骤
第一步:代码层防御 — 参数化查询
以PHP PDO为例,永远不要直接拼接SQL字符串:
<?php
// ❌ 错误做法 — SQL注入漏洞
$stmt = $pdo->query("SELECT * FROM users WHERE username = '{$_GET['username']}'");
// ✅ 正确做法 — 参数化查询
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $_GET['username']]);
$user = $stmt->fetch();
?>
Python(Flask + SQLAlchemy)示例:
from flask import Flask, request
from sqlalchemy import text
@app.route('/user')
def get_user():
# ❌ 错误 — 字符串拼接
# query = f"SELECT * FROM users WHERE id = {request.args.get('id')}"
# ✅ 正确 — 参数化
query = text("SELECT * FROM users WHERE id = :uid")
result = db.session.execute(query, {"uid": request.args.get('id')})
return result.fetchone()
Node.js(mysql2)示例:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({/* 配置 */});
// ✅ 使用 ? 占位符
const [rows] = await pool.execute(
'SELECT * FROM users WHERE email = ?',
[req.query.email]
);
第二步:输入层防御 — 白名单校验
对每类输入参数定义允许的格式,拒绝所有不匹配的输入:
// PHP 白名单校验示例
function validateInput($input, $type) {
switch ($type) {
case 'integer':
return filter_var($input, FILTER_VALIDATE_INT) !== false;
case 'email':
return filter_var($input, FILTER_VALIDATE_EMAIL) !== false;
case 'alphanumeric':
return preg_match('/^[a-zA-Z0-9_]+$/', $input) === 1;
default:
return false;
}
}
第三步:网络层防御 — ModSecurity WAF规则
安装ModSecurity并启用OWASP Core Rule Set(CRS):
# 安装 ModSecurity for Nginx
cd /usr/local/src
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
git clone --depth 1 https://github.com/SpiderLabs/owasp-modsecurity-crs.git
# 启用 CRS 规则
cp owasp-modsecurity-crs/crs-setup.conf.example /etc/nginx/modsec/crs-setup.conf
cp -r owasp-modsecurity-crs/rules /etc/nginx/modsec/
# Nginx 配置中加载
# nginx.conf 添加:
# modsecurity on;
# modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;
针对SQL注入添加自定义规则,拦截典型Payload:
# /etc/nginx/modsec/custom_sqli.conf
SecRule REQUEST_ARGS|REQUEST_BODY "@rx (union.*select|select.*from|insert.*into|delete.*from|drop\s+table|or\s+1=1|or\s+'1'='1)" \
"id:10001,phase:2,t:lowercase,deny,status:403,msg:'SQL Injection blocked'"
SecRule REQUEST_URI "@rx (%27|%22|--|;%20)" \
"id:10002,phase:1,t:urlDecode,deny,status:403,msg:'SQL special chars blocked'"
在Nginx server块中引用:
server {
listen 443 ssl;
server_name yourdomain.com;
# 启用ModSecurity
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/custom_sqli.conf;
location / {
proxy_pass http://backend;
}
}
配置验证
部署完成后,使用curl模拟SQL注入攻击验证拦截效果:
# 应返回 403 Forbidden
curl -v "https://yourdomain.com/user?id=1%20OR%201=1"
# 应返回 403
curl -v -X POST "https://yourdomain.com/login" \
-d "username=admin'--&password=test"
# 正常请求应返回 200
curl -v "https://yourdomain.com/user?id=42"
常见问题
Q1:WAF规则误杀正常请求怎么办?
先查看ModSecurity审计日志(/var/log/modsec_audit.log),确认触发规则的参数。如果是合法输入命中正则,将该参数加入白名单排除规则:SecRuleRemoveById 10001 "ENV:REQUEST_URI",或放宽特定URL的检测阈值。
Q2:参数化查询能否100%防御SQL注入?
参数化查询可以有效防御大多数注入场景,但以下情况仍需额外防护:动态表名列名(Prepared Statement不支持表名参数化)、存储过程动态SQL、ORDER BY子句排序字段。这些场景需配合严格白名单处理。
总结
SQL注入防御需要代码层(参数化查询)、输入层(白名单校验)、网络层(WAF规则)三层联动。核心原则是:永远不信任用户输入。建议在CI/CD流水线中集成SAST工具(如SonarQube、Semgrep)自动检测SQL拼接模式,结合WAF规则形成纵深防御体系。