SQL 注入防御方法
1. 使用预编译语句
这是最有效、也是最推荐的防御方法。预编译语句会先将 SQL 语句发送到数据库进行编译,然后将用户输入作为参数传递给编译好的语句。这样一来,用户输入的数据就无法改变 SQL 语句本身的结构
- 原理:将 SQL 语句与用户输入的数据分开处理。数据库会把用户输入的内容看作纯粹的数据,而不是可执行的代码
- 示例:
- 不安全的代码:
"SELECT * FROM users WHERE username = '" + userInput + "';"
- 如果
userInput
是' OR 1=1 --
,整个语句就变成了SELECT * FROM users WHERE username = '' OR 1=1 --;
,从而绕过登录验证
- 如果
- 安全的预编译语句:
"SELECT * FROM users WHERE username = ?;"
- 这里的问号
?
是一个占位符。无论用户输入什么,都会被当作username
字段的值来处理,而不是 SQL 代码
- 这里的问号
- 不安全的代码:
2. 对所有用户输入进行严格验证和过滤
永远不要相信用户的任何输入。在将数据送入数据库之前,必须对其进行验证和过滤
- 白名单验证:只允许特定的字符、格式或值通过。例如,如果某个输入框只接受数字,那么就只允许数字通过
- 黑名单过滤:禁止某些特定的危险字符或字符串,如单引号
'
、分号;
、双破折号--
等。但是,这种方法很容易被绕过,不推荐作为主要的防御手段
3. 使用 ORM 框架
许多现代编程语言的框架都提供了 ORM 工具,例如 Java 的 Hibernate、Python 的 SQLAlchemy、PHP 的 Eloquent。这些框架通常内置了对 SQL 注入的保护机制
- 优点:
- 安全性:ORM 框架会自动处理参数绑定,将开发者从手动编写安全 SQL 语句的繁琐工作中解放出来
- 易用性:开发者可以使用面向对象的方式操作数据库,无需直接编写 SQL 语句
4. 最小权限原则
为数据库账户分配最小的权限。一个账户如果只需要读取数据,就只给它 SELECT
权限,不要给它 INSERT
、UPDATE
或 DELETE
权限
- 好处:即使攻击者成功注入了 SQL 代码,也无法执行超出该账户权限范围的操作,如删除整个数据库
5. 错误信息处理
不要向用户暴露详细的数据库错误信息。攻击者可以利用这些信息来了解数据库结构、版本等,从而更容易发起下一次攻击
- 正确做法:当数据库查询失败时,只向用户显示一个通用的、友好的错误页面,并在后台日志中记录详细信息,供开发者排查
6. 使用 Web 应用防火墙(WAF)
WAF 可以在 Web 应用之前对 HTTP 请求进行过滤和拦截,它可以识别并阻止包含 SQL 注入特征的恶意请求
- 优点:
- 全面保护:可以为整个应用提供一层额外的保护
- 实时拦截:在攻击到达应用之前就将其阻止
- 局限性:
- WAF 的规则可能需要不断更新,以应对新的攻击方式
- 可能会有误报,影响正常用户的访问