在 Java 中,注入攻击是一种利用程序中的安全漏洞来执行恶意代码或获取未经授权的数据的攻击方式。
相关面试题:
#{} 和 ${} 的区别是什么?
${}
是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于原样文本替换,可以替换任意内容,比如${driver}会被原样替换为com.mysql.jdbc. Driver
。
#{}
是 sql 的参数占位符,MyBatis 会将 sql 中的#{}
替换为? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的? 号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name}
的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()
。
通常不会采用${}的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用
结论:实际开发中,能用#{}实现的,肯定不用${},因为${}会导致注入攻击,非常危险!
特殊情况: 动态的不是值,是列名或者关键字,需要使用${}拼接
一、常见的注入攻击类型
-
SQL 注入
- 原理:攻击者在输入数据时,构造恶意的 SQL 语句,欺骗应用程序执行这些语句,从而获取、修改或删除数据库中的数据。
- 例如:如果一个 Java 应用程序使用用户输入的参数来构建 SQL 查询,而没有对输入进行充分的验证和过滤。攻击者可以输入特定的字符串,使得最终执行的 SQL 语句变为意想不到的内容。比如,原本的查询是根据用户名查找用户信息,查询语句可能是 “SELECT * FROM users WHERE username = ' 输入的用户名 '”。如果攻击者输入 “' OR '1'='1'”,那么最终的 SQL 查询就变成了 “SELECT * FROM users WHERE username = '' OR '1'='1'”,这将返回数据库中的所有用户信息。
-
命令注入
- 原理:当 Java 应用程序调用外部系统命令时,如果用户输入被直接拼接到命令字符串中,攻击者可以通过构造恶意输入来执行任意系统命令。
- 例如:一个 Java 程序中有一个功能是根据用户输入的文件名进行文件操作。如果程序使用类似于 “Runtime.getRuntime ().exec ('some_command '+userInput)” 的方式执行命令,攻击者可以输入 “;rm -rf /”(在 Unix/Linux 系统中),这样就可能导致系统文件被删除。
-
XML 注入
- 原理:如果 Java 应用程序处理 XML 数据时没有对用户输入进行恰当的验证和过滤,攻击者可以构造恶意的 XML 输入,从而修改 XML 文档的结构或执行恶意的操作。
- 例如:在一个使用 XML 配置文件的 Java 应用中,如果用户可以输入 XML 数据,攻击者可能输入包含恶意实体引用的 XML 内容,从而导致应用程序在解析 XML 时执行意外的操作。
二、注入攻击的危害
- 数据泄露
- 攻击者可以获取敏感信息,如用户的个人资料、密码、财务数据等。
- 数据篡改
- 可以修改数据库中的数据,破坏数据的完整性。
- 系统破坏
- 执行恶意的系统命令可能导致系统崩溃、文件被删除或其他严重的后果。
三、防范注入攻击的方法
- 输入验证和过滤
- 对用户输入进行严格的验证,确保输入符合预期的格式和范围。可以使用正则表达式、白名单等方式进行输入验证。
- 例如,对于用户名输入,可以只允许字母、数字和特定的符号,并且限制长度。
- 参数化查询
- 在进行数据库操作时,使用参数化查询而不是拼接 SQL 语句。这样可以确保用户输入被正确地处理,不会被解释为 SQL 代码。
- 例如,使用 PreparedStatement 代替 Statement 进行数据库查询。
- 最小权限原则
- 确保应用程序以最小的权限运行,避免不必要的权限暴露给攻击者。
- 例如,数据库用户只具有执行特定操作的权限,而不是管理员权限。
- 安全编码规范
- 遵循安全的编码规范,如避免使用危险的函数和方法,及时更新依赖库等。
- 例如,不使用容易受到攻击的旧版本的库。