注入式(Inject)攻击是一类非常常见的攻击方式,其基本特征是允许攻击者将不可信的动态内容注入到程序中,并将其执行,这就可能完全改变最初预计的执行过程,产生恶意效果。
下面是几种主要的注入式攻击途径,原则上提供动态执行能力的语言特性,都需要地方发生注入攻击的可能。
1. SQL注入攻击。一个典型的场景就是Web系统的用户登录功能,根据用户输入的用户名和密码,我们需要去后端数据库核实信息。
假设应用逻辑是,后端程序利用界面输入动态生成类似下面的SQL,然后让JDBC执行。
select * from use_info where username="input_usr_name" and password="input_pwd".
但是,如果我输入的input_pwd 是类似下面的文本
" or ""="
那么拼接出的SQL字符串就变成了下面的条件,or的存在导致输入什么名字都是符合条件的。
select * from use_info where username="input_usr_name" and password="" or ""="".
它是利用了期望输入和可能输入之间的偏差。类似场景可以利用注入的不同SQL语句,进行各种不同目的的攻击,甚至还可以加上";delete xxx"之类语句,如果数据库权限控制不合理,攻击效果就可能是灾难性的。
2. 操作系统命令注入。
Java语言提供了类似Runtime.exec(...)的API,可以用来执行特定命令,假设我们构建了一个应用,以输入文本做为参数,执行下面的命令
ls -la input_file_name
但是用户如果输入“input_file_name;rm -rf/*” ,这就有可能出现问题了。Java标准类库本身进行了非常多的改进,所以类似这种编程错误,未必可以真的完成攻击,但其反应的一类场景是真实存在的。
3. XML注入攻击
Java核心类库提供了全面的XML处理、转换等各种API,而XML自身是可以包含动态内容的,例如XPATH,如果使用不当,可能导致访问恶意内容。
还有类似LDAP等允许动态内容的协议,都是可能利用特定命令,构造注入式攻击的,包括XSS(Cross-site Scripting)攻击,虽然并不和Java直接相关,但也可能在JSP等动态页面中发生。
知识扩展
1. Java应用安全,主要涉及哪些安全机制。
1) 运行时安全机制。
可以简单认为就是限制Java运行时的行为,不要做越权或者不靠谱的事情
----类加载过程中进行字节码验证,以防止不合规的代码影响JVM运行或者载入其他恶意代码
----类加载器本身也可以对代码之间进行隔离,例如,应用无法获取类加载器对象实例,不同的类加载器也可以起到容器的作用,隔离模块之间不必要的可见性。
----利用SecurityManager机制和相关的组件,限制代码的运行时行为能力,其中,可以定制policy文件和各种粒度的权限定义,限制代码的作用域和权限,例如对文件系统的操作权限,监听某个网络端口的权限等。Java的安全模型是以代码为中心,贯穿了从类加载到应用程序运行时权限检查等全过程。
----Java的GC等资源回收管理机制,都可以看作是运行时安全的一部分,如果相应机制失效,就会导致JVM出现OOM等错误,可看作是另类的拒绝服务。
2) Java提供的安全框架API,这是构建安全通信等应用的基础
----加密、解密API。
----授权、鉴权API。
----安全通信相关的类库,比如基本HTTPS通信协议相关标准实现,或者复数的类似证书撤销状态判断等协议实现。这一部分API内部实现是和厂商相关的,不同的JDK厂商往往会定制自己的加密算法实现。
3) JDK集成的各种安全工具
----keytool,这是个强大的工具,可以管理安全场景中不可或缺的秘钥、证书等,并且可以管理Java程序使用的keystore文件。
----jarsigner,用于对jar文件进行签名或者验证。
----在应用实践中,如果对安全要求非常高,建议打开SecurityManager,-Djava.security.manager,就会导致10%~15%的性能下降,在JDK9以后,这个开销有所改善。
2. 安全漏洞
1) 按照传统的定义,任何可以绕过系统安全策略限制的程序瑕疵,都可以算作安全漏洞。
2) 但是要达到攻击的目的,未必都需要绕过权限限制。比如利用哈希碰撞发起拒绝服务攻击,这种攻击方式无关于权限,可以看作是程序实现的瑕疵,给了攻击者以低成本进攻的机会。
3. 对SQL注入式攻击的解决办法
----在数据输入阶段,填补期望输入和可能输入之间的鸿沟。可以进行输入校验,限定什么类型的输入是合法的,不允许输入标点符号等特殊字符,或者特定结构的输入
----在Java应用进行数据库访问时,如果不用完全动态的SQL,而是利用PreparedStatement,可以有效防范SQL注入。不管是SQL注入还是OS命令注入,程序利用字符串拼接生成运行逻辑都是个可能的风险点。
----在数据库层面,如果对查询修改等权限进行了限制,就可以在一定程度上避免被注入删除等高破坏性的代码。
----尽量使用较新版本的JDK,并使用推荐 的安全机制和标准。。如果有看过JDK release notes,会发现JDK会修复已知的安全漏洞,并且会对安全机制进行增强