SQL 注入(SQL Injection)长期位居 OWASP Top 10 前列,尽管其原理并不复杂,但在真实业务代码中仍然屡禁不止。很多团队把防御希望寄托在 WAF 或前端校验上,却忽略了最根本的一条原则:安全必须内建于代码本身。本文从代码层出发,系统梳理防御 SQL 注入的最佳实践,并对比 XSS 的常见方式与前端防御思路,最后附上一份 Linux 服务器安全加固清单,帮助你把整条链路的安全水位提上来。
一、为什么代码层防御才是根本
WAF 可以拦截已知攻击特征,前端校验可以提升用户体验,但两者都可能被绕过:攻击者可以编码变形、分块传输、利用白名单外的入口;而前端代码完全暴露在用户浏览器中,任何校验都能被篡改。只有服务端代码在构造 SQL 语句时做到“数据与指令分离”,才能从根源上消除注入。
二、代码层防御 SQL 注入的核心实践
1. 首选参数化查询(预编译语句)
参数化查询是防御 SQL 注入最有效的手段。其核心思想是:SQL 语句的结构先被数据库编译固定,用户输入只作为参数值传入,永远不会被当作 SQL 语法解析。
以 Java 为例:
String sql = "SELECT * FROM users WHERE username = ? AND status = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, username);
ps.setInt(2, status);
ResultSet rs = ps.executeQuery();
Python 中同样应使用参数占位符,而不是字符串拼接:
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
需要特别强调的是,参数化查询不能用于拼接表名、列名、ORDER BY 字段。这些位置如果必须动态化,应使用严格的白名单映射,而不是直接拼接用户输入。
2. 坚决杜绝字符串拼接
以下写法都是高危的:
String sql = "SELECT * FROM users WHERE name = '" + name + "'";
sql = "SELECT * FROM users WHERE id = " + user_id
无论输入来自表单、URL 参数、Header 还是 Cookie,只要进入 SQL 语句就必须经过参数化或严格校验。很多注入漏洞并非出现在登录接口,而是出现在导出、统计、排序等“次要”功能中。
3. 使用 ORM 框架并正确使用
MyBatis、Hibernate、SQLAlchemy、GORM 等 ORM 框架默认使用参数绑定,能大幅降低注入风险。但要注意:
- MyBatis 中优先使用
#{}而不是${},${}是直接拼接,仅可用于可信的动态表名等场景; - 使用原生 SQL 查询方法时,仍需传参而不是拼接;
- 避免在 ORM 中执行来路不明的原生 SQL 字符串。
4. 输入校验与类型约束
输入校验不能替代参数化查询,但可以作为纵深防御的一层:
- 对数字型参数强制类型转换,如
Integer.parseInt(id); - 对枚举型参数使用白名单,例如排序字段只允许
create_time、id等固定值; - 限制输入长度,拒绝包含明显 SQL 元字符的异常输入并记录日志。
5. 最小权限与错误信息控制
数据库账号应遵循最小权限原则:Web 应用账号通常只需要 SELECT/INSERT/UPDATE/DELETE,不应拥有 DROP、FILE、GRANT 等权限。同时,生产环境不要将数据库报错信息直接返回给前端,避免攻击者通过报错进行盲注探测。
6. 统一封装与代码审计
将数据库访问统一封装到 DAO 层,禁止业务代码直接拼接 SQL;在 CI 流程中引入 SAST 工具(如 SonarQube、Semgrep)扫描拼接行为;对历史代码定期做注入专项审计。
三、XSS 的常见方式与前端防御
SQL 注入和 XSS 常被一起提及,因为二者都源于“把不可信数据当成了代码”。XSS 常见方式包括:
- 反射型:恶意脚本随 URL 参数进入页面并立即执行;
- 存储型:恶意内容被存入数据库,其他用户访问时触发;
- DOM 型:前端 JavaScript 直接使用
innerHTML、document.write等将用户输入写入 DOM。
前端防御要点:
- 输出编码:根据上下文对 HTML、属性、URL、JavaScript 分别编码;
- 避免使用
innerHTML,优先使用textContent; - 使用 CSP(内容安全策略)限制脚本来源;
- 对富文本使用成熟的白名单过滤库,如 DOMPurify;
- 设置 Cookie 的
HttpOnly,降低脚本窃取会话的风险。
需要明确的是,前端防御主要保护当前用户,服务端输出编码和输入过滤同样不可缺失。
四、Linux 服务器安全加固清单
代码安全离不开运行环境的安全,以下清单可作为基础加固参考:
- 及时更新系统与软件补丁,关闭不必要的端口和服务;
- 禁用 root 远程登录,使用普通用户加 sudo,推荐密钥登录并禁用密码认证;
- 配置防火墙(iptables/firewalld/ufw),仅放行必要端口;
- 使用 Fail2Ban 防止 SSH 暴力破解;
- 数据库不对外网开放,仅监听内网或本地;
- 开启审计日志,集中收集并设置告警;
- 对关键目录设置最小权限,Web 目录禁止执行脚本上传;
- 定期备份并验证可恢复性;
- 使用 SELinux/AppArmor 做强制访问控制;
- 部署 WAF 作为补充,但不能替代代码层防御。
五、总结
防御 SQL 注入没有银弹,但有明确的主线:参数化查询是核心,输入校验是补充,最小权限是底线,代码审计是保障。同时把 XSS 防御和服务器加固纳入同一套安全体系,才能形成从代码到运行时的完整防线。安全不是一次性的项目,而是持续融入开发流程的习惯。
未经允许不得转载:任鹏个人博客 » 代码层防御 SQL 注入的最佳实践总结


朋友圈点赞图在线生成源码