数码课堂
第二套高阶模板 · 更大气的阅读体验

防止SQL注入编码规范:程序员必须掌握的安全细节

发布时间:2025-12-16 23:28:56 阅读:1 次

开发网站时,很多人只关注功能能不能跑通,却忽略了数据库查询的安全性。一个典型的例子是用户登录功能。假设你写了一段代码,把用户名和密码直接拼接到 SQL 语句中:

String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";

看起来没问题,但如果有人在登录框里输入用户名 ' OR '1'='1,那整个 SQL 就变成了:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...'

由于 '1'='1' 永远成立,攻击者可能绕过验证直接登录。这就是典型的 SQL 注入攻击。

别再手动拼接 SQL 字符串

很多漏洞都源于字符串拼接。无论用的是 Java、PHP 还是 Python,只要把用户输入直接塞进 SQL 语句,风险就埋下了。正确的做法是使用参数化查询(Prepared Statements),让数据库引擎自动处理输入内容的转义。

比如在 Java 中,应该这样写:

String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, username);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();

这里的问号是占位符,实际值由数据库驱动安全填充,根本不会被当作 SQL 代码执行。

ORM 框架也不能完全依赖

有些人觉得用了 Hibernate 或 MyBatis 就高枕无忧,其实不然。如果在 HQL 或 XML 映射文件里又手动拼接字符串,照样会出事。比如这段 MyBatis 的写法:

<select id="findUser" resultType="User">
  SELECT * FROM users WHERE username = ${username}
</select>

注意这里的 ${} 是直接替换,非常危险。应该改用 #{} 来启用参数化:

<select id="findUser" resultType="User">
  SELECT * FROM users WHERE username = #{username}
</select>

对输入做基本过滤和类型检查

虽然参数化能挡住大部分攻击,但加上输入校验更保险。比如手机号字段,如果用户输了个 SQL 语句进去,系统就应该直接拒绝。可以在前端提示,更要在后端验证。

例如,邮箱字段必须符合基本格式,年龄只能是数字,搜索关键词长度要有上限。这些规则不仅防注入,还能提升整体健壮性。

错误信息别暴露太多细节

调试时看到“SQL syntax error near 'xxx'”挺方便,但上线后这等于给攻击者指路。应该统一返回模糊错误,比如“操作失败”,具体日志记在服务器上就行。

曾经有个电商网站,搜索商品时报错直接打印了完整 SQL,连表名字段名都暴露了。攻击者顺着这条线挖出了整个数据库结构,最后拖走了几万条用户数据。

定期审查代码里的 SQL 片段

团队协作开发时,新人可能不了解规范,随手写了拼接语句。建议在代码评审中加入一条:所有数据库查询是否都使用了参数化?可以用工具扫描项目里有没有 SELECT.*+ variable 这类危险模式。

一个小疏忽可能带来大麻烦。安全不是某个环节的事,而是每个开发者在写每一行代码时都要绷着的那根弦。