中小企业做SQL注入防护,最大的误区是一上来就想买WAF、上态势感知、搞渗透测试。这些东西不是没用,而是对预算有限、没有专职安全人员的小团队来说,落地成本太高,最后往往变成摆设。真正能落地的防护,是把数据库访问层管好,把输入输出边界卡死,再用几个免费工具做日常验证。下面直接讲具体怎么做。

先把数据库账号权限砍到最低

绝大多数SQL注入能造成严重后果,不是因为漏洞存在,而是因为数据库账号权限过大。很多中小企业的Web应用连数据库用的是root或者sa账号,一旦被注入,攻击者直接就能跨库、读系统表、甚至执行系统命令。落地第一步,就是给每个业务系统创建独立的数据库账号,只授予该业务库的增删改查权限,禁止DDL权限,禁止访问mysql.user、sys.tables这类系统表。如果是MySQL,授权语句大致如下:

CREATE USER 'app_user'@'%' IDENTIFIED BY '强密码';
GRANT SELECT, INSERT, UPDATE, DELETE ON business_db.* TO 'app_user'@'%';
REVOKE ALL ON mysql.* FROM 'app_user'@'%';

这一步不需要买任何设备,只需要改配置,但能直接阻断注入后的提权和横向移动。很多中小企业被脱库,事后复盘发现攻击者就是通过注入拿到了root权限,然后一条SQL就把所有库导出了。

参数化查询是核心,不是可选项

SQL注入的根源是字符串拼接SQL语句。只要代码里还有类似“SELECT * FROM users WHERE name='” + input + “'”的写法,防护就永远有漏洞。落地层面,必须强制所有开发人员使用参数化查询。Java用PreparedStatement,PHP用PDO的prepare,Python用cursor的execute带参数形式。下面给一个PHP的对比示例:

// 错误写法,存在注入
$sql = "SELECT * FROM users WHERE name = '" . $_GET['name'] . "'";
$result = mysqli_query($conn, $sql);

// 正确写法,参数化查询
$stmt = $conn->prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $_GET['name']);
$stmt->execute();

有些开发人员会问,LIKE查询、IN查询、动态排序字段怎么办。LIKE查询照样可以用参数化,把百分号拼在参数值里传入。IN查询可以动态生成占位符,然后把每个值绑定进去。排序字段和表名这类无法参数化的部分,必须做白名单校验,只允许传入预设的几个值。这些细节如果不落地,参数化查询就会因为“不方便”被开发人员绕过。

输入验证和输出编码要分开做

很多人把输入过滤当成防注入的主要手段,看到单引号就转义,看到SELECT就拦截。这种做法既误伤正常用户,又防不住编码绕过的攻击。正确的做法是:输入验证只做业务层面的合法性检查,比如手机号必须是11位数字,年龄必须在0到150之间,邮箱必须符合格式。SQL注入防护交给参数化查询,XSS防护交给输出编码。不要在输入阶段做SQL关键字过滤,那是最低效且容易被绕过的方式。

错误信息必须屏蔽,但不能完全关掉日志

很多中小企业的网站在测试环境为了方便调试,直接把数据库报错显示在页面上。攻击者利用报错注入,可以从错误信息里拿到表名、字段名甚至数据。落地方法是:生产环境关闭display_errors,设置log_errors=On,把错误写入服务器日志。同时,数据库连接的错误信息也要自定义,不能直接把数据库返回的报错抛给前端。日志要保留,但日志内容不要包含用户的完整输入,否则日志本身也可能被利用。

用免费的SQLMap做自查,别等攻击者帮你测

SQLMap是开源的自动化SQL注入检测工具,中小企业完全可以用它对自己的业务系统做定期自查。不需要买商业扫描器,一条命令就能跑起来:

sqlmap -u "http://你的域名/product.php?id=1" --batch --level=3 --risk=2

跑完之后看报告,如果发现注入点,立刻修复。建议每个月跑一次,尤其是新功能上线后。注意,用SQLMap测自己的系统时,要避开业务高峰期,并且提前备份数据库,因为某些测试payload可能产生大量请求或写入测试数据。另外,SQLMap只能检测GET和POST参数里的注入,对于JSON、Cookie、Header里的注入点,需要手动指定参数位置。

Web应用防火墙可以后置,但要有基础规则

对中小企业来说,WAF不是第一优先级,但如果有条件,可以在Nginx层加一个开源的ModSecurity,配置OWASP核心规则集。这套组合免费,部署成本不高,能拦截一部分自动化攻击和已知攻击特征。但不要指望WAF能挡住所有注入,它只是最后一道防线。真正的防护还是前面说的参数化查询和权限控制。如果WAF误报太多,可以先开检测模式,跑一段时间再切换为拦截模式。

数据库审计和慢查询日志要开起来

很多中小企业直到数据被删了才知道被注入。其实攻击者在注入成功后,往往会执行大量异常查询,比如information_schema的遍历、大量UNION SELECT、延时函数等。开启数据库的慢查询日志和通用查询日志,虽然会有一定性能开销,但对中小企业来说通常可以接受。定期检查日志里是否有大量来自同一个IP的异常SQL,或者包含sleep、benchmark、load_file等函数的语句。这一步不需要额外工具,用grep和awk就能做初步分析。

把SQL注入防护纳入开发流程,而不是事后补救

最落地的做法,是在代码提交环节加一道自动检查。可以用开源的静态代码扫描工具,比如SonarQube的SQL注入规则,或者用简单的正则扫描代码里的字符串拼接SQL模式。同时,在测试环境部署一个简单的自动化测试用例,用SQLMap对每个接口跑一遍。这些动作不需要专职安全人员,开发组长或者运维就能配置。关键是形成习惯,每次上线前跑一遍,而不是等出了事再排查。

备份策略是最后的兜底

即使前面所有防护都做了,也不能保证百分百不被注入。所以备份必须做,而且要定期验证备份可恢复。中小企业至少做到每天一次全量备份,保留最近30天的备份文件,备份文件放在与生产服务器不同的机器上。如果用的是云数据库,开启自动备份和异地备份。这样即使被注入删库,也能在最短时间内恢复业务,把损失降到最低。

总结一下,中小企业做SQL注入防护,不需要大而全的安全方案,只需要把数据库权限、参数化查询、错误处理、自查工具、基础日志和备份这六件事做扎实。每一项都有明确的落地动作,不依赖高预算,也不依赖专业安全团队。真正难的不是技术,而是把这些动作变成日常习惯。