`
honey_bee
  • 浏览: 90294 次
社区版块
存档分类
最新评论

编写安全的Java代码

阅读更多
本文是来自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/docs/guide/extensions/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) {
 ...

               stream.write(internalArray);
                ...
}
}

黑客代码

       public class HackerObjectOutputStream extends ObjectOutputStream{
            public void write (byte [] b) {
               Modify b
      }
}
 ...
             YourClass yc = new YourClass();
              ...
 
             HackerObjectOutputStream hoos = new HackerObjectOutputStream();

              hoos.writeObject(yc);

  • 字节流加密

  保护虚拟机外的字节流的另一方式是对序列化包产生的流进行加密。字节流加密防止解码或读取被序列化的对象的私有状态。如果决定加密,应该管理好密钥,密钥的存放地点以及将密钥交付给反序列化程序的方式等。

  • 需要提防的其他事宜

  如果不可信任代码无法创建对象,务必确保不可信任代码也不能反序列化对象。切记对对象反序列化是创建对象的另一途径。

  比如说,如果一个applet创建了一个frame,在该frame上创建了警告标签。如果该frame被另一应用程序序列化并被一个applet反序列化,务必使该frame出现时带有同一个警告标签。

  原生方法

  应从以下几个方面检查原生方法:

 

  •   它们返回什么
  •   它们需要什么参数
  •   它们是否绕过了安全检查
  •   它们是否是公共的,私有的等
  •   它们是否包含能绕过包边界的方法调用,从而绕过包保护

  清除敏感信息

  当保存敏感信息时,如机密,尽量保存在如数组这样的可变数据类型中,而不是保存在字符串这样的不可变对象中,这样使得敏感信息可以尽早显式地被清除。不要指望Java平台的自动垃圾回收来做这种清除,因为回收器可能不会清除这段内存,或者很久后才会回收。尽早清除信息使得来自虚拟机外部的堆检查攻击变得困难。

分享到:
评论

相关推荐

    编写安全的代码

    安全框架的选择也是提高代码安全性的关键。许多成熟的框架,如Spring Security for Java、Django for Python等,提供了内置的安全机制,如身份验证、授权、CSRF防护等。利用这些框架可以大大降低安全风险,但同时也...

    java代码编写规范

    Java程序员编写代码时应当遵循的一些基本规范

    编写java代码常用的工具代码

    总结了编写java代码常用的算法代码,如ucs2,ascii,进制转换,以及APN相关的管理代码

    JAVA编写的五子棋代码

    本项目名为"JAVA编写的五子棋代码",旨在提供一个友好的界面,让学生或初学者能够理解和实践基本的Java编程技巧。 首先,我们要关注的是GUI的实现。Java提供了Swing和JavaFX库来创建图形用户界面。在这个项目中,...

    java程序代码编写的详细规范

    详细介绍编写代码的注意点,让代码看起来有条理,更清晰! 编码规范对于程序员而言尤为重要,有以下几个原因:(1)一个软件的生命周期中,80%的花费在于维护。(2)几乎没有任何一个软件,在其整个生命周期中,均由...

    编写高质量代码 改善Java程序的151个建议 PDF高清完整版

    书中详细列举了151条实用的建议,旨在帮助开发者编写出更高效、可读性更强且易于维护的Java代码。以下是对这些建议的详细解读和扩展讨论: 1. **注释与文档**:良好的注释是代码可读性的关键。书中的建议可能包括为...

    java 编写的txt代码

    java 编写的txt代码,希望对你有所帮助

    自己编写的测试代码

    通常使用框架如JUnit(Java)或pytest(Python)来编写单元测试,这些框架提供了断言和模拟等工具,使得测试更加便利。 2. 集成测试:当单个组件经过验证后,集成测试用于检查不同组件之间的交互。这个阶段的目标是...

    Java代码转换成Objective-C的工具

    这意味着,开发者编写的Java代码可以成为iOS应用程序的一部分,且无需对生成的文件进行编辑。 J2ObjC 支持大多数 Java 语言和运行环境的客户端应用特性,包括异常、内部类和匿名类、泛型、线程和反射,也支持 JUnit...

    java代码编写的30条建议

    java代码编写的30条建议,很简单,但是也很重要,每一条建议都是千锤百炼得出的

    java编写的记事本代码

    java 2 se编写的一个小程序,比较简单,使用等等等等特点。

    java编写的计算器代码

    java编写的计算器代码

    java代码-使用java编写菱形形状的源代码

    java代码-使用java编写菱形形状的源代码 ——学习参考资料:仅用于个人学习使用!

    JAVA编写五子棋的4种方法代码合集

    用JAVA编写五子棋的4种方法代码合集 在JAVA netBeans里编译通过

    java编写的深度学习代码例子大全

    java编写的深度学习代码例子大全,Java深度学习的程序例子都有了。

    #资源分享达人# 代码审计[java安全编程].doc.zip

    这个压缩包可能包含了一份详细的文档,指导开发者如何在编写Java代码时遵循最佳的安全实践,以防止潜在的安全漏洞。 【描述】:“#资源分享达人# 代码审计java”进一步强调了这个主题集中在Java语言的代码审计过程...

    用Java代码编写的记事本

    用Java编写的简易记事本,类似于我们平常用的记事本。

    Java2Pas Java代码转pas代码

    此外,如果Java代码中包含了一些特定于Java平台的API调用,这些部分在Pascal中可能需要替换为相应的函数或库。 总的来说,Java2Pas是一个方便的工具,能够帮助开发者跨越Java和Pascal之间的语言障碍,提高代码复用...

    C++代码转java工具

    在某些情况下,开发者可能需要将已有的C++代码转换为Java代码,以便在Java平台上运行或利用Java的生态系统。 标题“C++代码转Java工具”暗示了一个软件或服务的存在,它的功能是自动化C++源代码到Java源代码的转换...

Global site tag (gtag.js) - Google Analytics