java安全

谈谈java安全,对于一些程序员,当听到这样一个问题?是不是一下子懵逼了,首先第一感觉是不是很高大尚。这个命题显得很广泛,也没有确切的唯一的答案,开放性。在面试中,也常出现,特别是一些大的企业面试官喜欢问。

关于如何编写安全的Java代码,Sun官方给了一份指南,有兴趣的同学可以参考这篇文章,大致为:

    • 静态字段
    • 缩小作用域
    • 公共方法和字段
    • 保护包
    • equals方法
    • 如果可能使对象不可改变
    • 不要返回指向包含敏感数据的内部数组的引用
    • 不要直接存储用户提供的数组
    • 序列化
    • 原生函数
    • 清除敏感信息

  比如,DoS是一种常见的网络攻击,有人戏称为“洪水攻击”。其惯用手法是通过某种手段,比如大量的机器发送请求,将目标网站宽带和其资源耗尽,导致用户无法正常访问,甚至服务器的宕机。

  而对于此类问题,如果单从服务器级别考虑,多少欠缺,我们或许需要考虑程序级别的攻击,比如Java,JVM,以及涉及到的线程方面的安全,应用程序的瑕疵等进行低成本的DoS攻击。

  而在面试中,我们都会被问到安全性的问题,却大多比较多泛泛,大而广,而大多数的安全性问题都与代码安全性有关。我们回顾下Java代码的运行过程:

  首先编译器把.java文件编程成.class字节码文件,然后由类加载器负责把.class文件加载到JVM,再由字节码校验进行校验,然后由Java解释器负责把该类文件解释为机器码执行。

  在类加载器加载.class文件到java虚拟机的过程中,类加载器通过区分本机文件系统的类和网络系统导入的类增加安全性(不允许网络上的应用程序修改本地的数据),本机的类先被加载,一旦所有的类加载完,执行文件的内存划分就固定了,然后字节码校验器开始校验.class字节码文件,字节码校验器不检查那些可信任的编译器所产生的类文件。通过之后,java解释器材负责把类文件解释成为机器码进行执行。


静态字段
• 避免使用非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) {
...

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平台的自动垃圾回收来做这种清除,因为回收器可能不会清除这段内存,或者很久后才会回收。尽早清除信息使得来自虚拟机外部的堆检查攻击变得困难。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值