技术摘要:如何编写安全的Java代码

2008-02-23 08:12:53来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

  摘要:本文是来自Sun官方站点的一篇关于如何编写安全的Java代码的指南,开发者在编写一般代码时,可以参照本文的指南

  • 静态字段

  • 缩小作用域

  • 公共方法和字段

  • 保护包

  • equals方法

  • 如果可能使对象不可改变

  • 不要返回指向包含敏感数据的内部数组的引用

  • 不要直接存储用户提供的数组

  • 序列化

  • 原生函数

  • 清除敏感信息

  静态字段

  • 避免使用非final的公共静态变量

  应尽可能地避免使用非final公共静态变量,因为无法判断代码有无权限改变这些变量值。

  • 一般地,应谨慎使用易变的静态状态,因为这可能导致设想中相互独立的子系统之间发生不可预知的交互。

  缩小作用域

  作为一个惯例,尽可能缩小方法和字段的作用域。检查包访问权限的成员能否改成私有的,保护类型的成员可否改成包访问权限的或者私有的,等等。

  公共方法/字段

  避免使用公共变量,而是使用访问器方法访问这些变量。用这种方式,如果需要,可能增加集中安全控制。

  对于任何公共方法,如果它们能够访问或修改任何敏感内部状态,务必使它们包含安全控制。

  参考如下代码段,该代码段中不可信任代码可能设置TimeZone的值:

  private static TimeZone defaultZone = null;
  public static synchronized void setDefault(TimeZone zone)
  {
  defaultZone = zone;
  }

  保护包

  有时需要在全局防止包被不可信任代码访问,本节描述了一些防护技术:

  • 防止包注入:如果不可信任代码想要访问类的包保护成员,可以尝试在被攻击的包内定义自己的新类用以获取这些成员的访问权。防止这类攻击的方式有两种:

  1. 通过向java.security.properties文件中加入如下文字防止包内被注入恶意类。

  ...

  package.definition=Package#1 [,Package#2,...,Package#n]

  ...

  这会导致当试图在包内定义新类时类装载器的defineClass方法会抛出异常,除非赋予代码一下权限:

  ...

  RuntimePermission("defineClassInPackage." package)

  ...

  2. 另一种方式是通过将包内的类加入到封装的Jar文件里。

  (参看http://java.sun.com/j2se/sdk/1.2/do...ons/spec.html)

  通过使用这种技巧,代码无法获得扩展包的权限,因此也无须修改java.security.properties文件。

  • 防止包访问:通过限制包访问并仅赋予特定代码访问权限防止不可信任代码对包成员的访问。通过向java.security.properties文件中加入如下文字可以达到这一目的:

  ...

  package.access=Package#1 [,Package#2,...,Package#n]

  ...

  这会导致当试图在包内定义新类时类装载器的defineClass方法会抛出异常,除非赋予代码一下权限:

  ...

  RuntimePermission("defineClassInPackage." package)

  ...

  如果可能使对象不可改变

  如果可能,使对象不可改变。如果不可能,使得它们可以被克隆并返回一个副本。如果返回的对象是数组、向量或哈希表等,牢记这些对象不能被改变,调用者修改这些对象的内容可能导致安全漏洞。此外,因为不用上锁,不可改变性能够提高并发性。参考Clear sensitive information了解该惯例的例外情况。

  不要返回指向包含敏感数据的内部数组的引用

  该惯例仅仅是不可变惯例的变型,在这儿提出是因为常常在这里犯错。即使数组中包含不可变的对象(如字符串),也要返回一个副本这样调用者不能修改数组中的字符串。不要传回一个数组,而是数组的拷贝。

  不要直接在用户提供的数组里存储

  该惯例仅仅是不可变惯例的另一个变型。使用对象数组的构造器和方法,比如说PubicKey数组,应当在将数组存储到内部之前克隆数组,而不是直接将数组引用赋给同样类型的内部变量。缺少这个警惕,用户对外部数组做得任何变动(在使用讨论中的构造器创建对象后)可能意外地更改对象的内部状态,即使该对象可能是无法改变的。

  序列化

  当对对象序列化时,直到它被反序列化,它不在Java运行时环境的控制之下,因此也不在Java平台提供的安全控制范围内。

  在实现Serializable时务必将以下事宜牢记在心:

  • transient

  在包含系统资源的直接句柄和相对地址空间信息的字段前使用transient关键字。 如果资源,如文件句柄,不被声明为transient,该对象在序列化状态下可能会被修改,从而使得被反序列化后获取对资源的不当访问。

  • 特定类的序列化/反序列化方法

  为了确保反序列化对象不包含违反一些不变量集合的状态,类应该定义自己的反序列化方法并使用ObjectInputValidation接口验证这些变量。

  如果一个类定义了自己的序列化方法,它就不能向任何DataInput/DataOuput方法传递内部数组。所有的DataInput/DataOuput方法都能被重写。注意默认序列化不会向DataInput/DataOuput字节数组方法暴露私有字节数组字段。

  如果Serializable类直接向DataOutput(write(byte [] b))方法传递了一个私有数组,那么黑客可以创建ObjectOutputStream的子类并覆盖write(byte [] b)方法,这样他可以访问并修改私有数组。下面示例说明了这个问题。

  你的类:

  public class YourClass implements Serializable {
  private byte [] internalArray;
  ....
  private synchronized void writeObject(ObjectOutputStream stream) {
  ...

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:关于Java23种设计模式的有趣见解

下一篇:让你不苦恼:Java的中文编程配置心得