注入攻击防御详解
注入攻击是一类常见且危害极大的网络攻击方式,其核心原理是攻击者将恶意代码注入到应用程序的输入中,从而干扰程序的正常执行流程,达到窃取数据、篡改系统或获取权限等目的。常见的注入攻击主要包括SQL 注入和OS 注入,以下是对这两种攻击的详细解析及防御措施。
SQL 注入
什么是 SQL 注入?
SQL 注入是攻击者通过在 Web 应用的输入参数中插入恶意 SQL 语句,欺骗数据库服务器执行非预期操作的攻击方式。由于应用程序未对用户输入进行严格过滤,恶意 SQL 语句会被当作合法指令执行,从而导致数据库信息泄露、数据篡改、权限提升等严重后果。
示例:
假设一个登录功能的 SQL 查询语句为:
1 | SELECT * FROM users WHERE username = '用户输入的用户名' AND password = '用户输入的密码'; |
若攻击者输入用户名为zhangsan,密码为' or '1'='1,则拼接后的 SQL 语句变为:
1 | SELECT * FROM users WHERE username = 'zhangsan' AND password = '' or '1'='1'; |
由于'1'='1'恒为真,该语句会返回所有用户信息,导致攻击者无需正确密码即可登录。
SQL 注入的防御措施
(1)使用预编译语句(参数化查询)
预编译语句(如 Java 中的PreparedStatement)是防御 SQL 注入的最有效手段。其原理是:
- 先将 SQL 语句的结构(不含参数)发送给数据库编译,生成执行计划;
- 执行时仅传入参数值,数据库会将参数视为纯数据,而非 SQL 指令的一部分,从而避免攻击者篡改 SQL 结构。
示例(Java):
1 | String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; |
(2)使用 ORM 框架
ORM(对象关系映射)框架(如 MyBatis、Hibernate)内置了参数过滤机制,能自动对输入参数进行转义,降低 SQL 注入风险。
MyBatis:使用
#{}占位符接收参数时,会自动对参数进行转义;若使用${}(字符串拼接)则存在风险,需避免。
安全示例:1
2
3<select id="getUser" parameterType="String" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>Hibernate:通过 HQL(Hibernate 查询语言)的参数绑定机制(如
setParameter方法),自动处理参数转义。
(3)对用户输入进行严格过滤与验证
- 输入验证:限制输入的长度、格式(如邮箱、手机号等使用正则表达式校验),拒绝不符合规则的输入。
- 特殊字符过滤:对输入中的 SQL 关键字(如
SELECT、INSERT、OR、'、;等)进行转义或过滤(例如将'替换为'')。
(4)避免明文存储敏感信息
密码等敏感数据需通过加密算法(如 SHA-256、BCrypt)进行单向哈希处理后存储,而非明文。即使数据库被入侵,攻击者也无法直接获取原始密码。
示例:使用 BCrypt 加密密码:
1
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
(5)限制数据库权限
- 为应用程序的数据库账号分配最小权限(如仅授予
SELECT、INSERT等必要操作权限,禁止DROP、ALTER等高危操作),即使发生 SQL 注入,攻击者的破坏范围也会被限制。
(6)妥善处理异常信息
- 避免将数据库错误信息(如表结构、字段名、服务器版本等)直接返回给用户,防止攻击者通过异常信息分析系统结构。应自定义错误页面,仅返回模糊的错误提示(如 “操作失败,请稍后重试”)。
OS 注入
什么是 OS 注入?
OS 注入(操作系统命令注入)是攻击者通过输入恶意指令,让应用程序执行非预期的操作系统命令的攻击方式。常见于应用程序调用系统命令(如ping、ls等)且未对输入进行过滤的场景。
示例:
假设一个应用程序提供 “ping 某个 IP” 的功能,其后台代码为:
1 | import os |
若攻击者输入8.8.8.8 && rm -rf /,则拼接后的命令变为:
1 | ping 8.8.8.8 && rm -rf / |
这会导致先执行ping,再执行删除系统文件的恶意操作。
OS 注入的防御措施
(1)避免直接拼接用户输入到系统命令中
- 尽量使用编程语言内置的 API 替代系统命令(如 Java 中用
InetAddress类实现 ping 功能,而非调用ping命令)。 - 若必须执行系统命令,使用参数化调用(如 Java 的
ProcessBuilder),避免直接拼接用户输入。
安全示例(Java):
1 | List<String> command = new ArrayList<>(); |
(2)严格过滤用户输入
- 限制输入的字符范围(如仅允许数字、
.等合法字符,禁止&、|、;等命令分隔符)。 - 使用白名单机制,仅允许预设的合法输入(如仅允许特定 IP 段)。
(3)限制应用程序权限
- 运行应用程序的账号应使用最低权限(如普通用户权限),避免使用
root或管理员权限,降低攻击成功后的破坏范围
v1.3.10