实现安全的Java代码,需要从功能设计到实现细节,都充分考虑可能的安全影响。
以消耗系统资源为目的的攻击是很常见的方式。
CPU、线程、内存、文件描述符、数据库连接、再入锁等各种资源都可能成为攻击者消耗的目标。
常见代码问题
数值运算溢出
如:
Java代码
-
if (a+b<c) {
-
...
-
}
a+b 可能超出上限。改成 a<c-b 也有溢出的风险。
需要根据各变量的范围进行处理。
异常中包含敏感信息
如:
Java代码
-
try {
-
...
-
} catch (Exception e) {
-
throw new RuntimeException(hostname + port + " not available");
-
}
如果异常中包含了敏感信息,最终又未最好脱敏处理,就存在信息安全风险。
如果没有明确的必要性,一般不建议输出这些内部的敏感信息。
如,华为的“安全红线”就不允许日志中存在文件名等信息。
在一些安全标准特别高的系统中,敏感信息被使用后,需立即明确在内存中销毁,以免被探测或在core dump时意外暴露。
如,在使用密码的场景中,可以用 char[] 来临时存放密码,而不是String。
使用完后,用随机字符覆盖char[]。这样可以避免因String的不可变性,导致密码一直留在堆,直到被垃圾回收。
序列化安全
Java序列化也是安全问题频发的场景。
通常建议:
-
敏感信息不要被序列化。可以用transient关键字防止相关字段被序列化。
-
反序列化时,在readObject中实现与对象构建过程相同的安全检查和数据校验。
JDK 9 中还可以通过 ObjectInputFilter 来限定允许被序列化的类型(黑白名单)。
工程实践
代码安全问题细节繁杂,全凭人肉记忆很不现实。
在实际工程中,需借助工具和流程管控等综合处理。
可根据实际情况选择合适的方案,平衡安全与迭代节奏。
开发测试阶段
一些典型的做法:
-
早期设计阶段,由安全专家组对新特性进行风险评估。
很多组织并没有这个能力/资源做风险评估。
甚至关键决策人根本不具备这方面意识和能力。
全靠一线资深员工尽心尽责。
这种能力配不上职位的领导管理是非常危险的。
-
在开发过程及code review阶段,应用代码规范和相关工具进行检查。
《阿里巴巴Java开发手册》及相关配套工具就是常见工具之一。
-
利用多种静态分析工具,如 FindBugs,检查潜在的安全风险。
-
JDK的警告也需要当作错误认真处理。
-
在代码提交入库等关键环节,利用hook机制调用检查工具,阻止不合规的代码入库。
静态分析工具并不需要大而全,“足够好”就行。没有工具能发现所有问题。
在保证功能的前提下,需考虑降低分析过程的噪音信息,提高分析效率。
部署阶段
JDK自身缺陷的处理可以算部署阶段的安全事务。
关注JDK更新的安全漏洞补丁并及时更新是安全管理的一部分。
对安全敏感的产品,还需关注JDK在加解密方面的路线图。