进阶:Java编写过程中安全问题解决指南
在中,我们讨论了对付 13 种不同静态暴露的技巧。对于每种暴露,我们解释了不处理这些安全性问题所造成的影响。我们还为您推荐了一些准则,要开发不受这些静态安全性暴露威胁的、健壮且安全的 Java 应用程序,您应该遵循这些准则。一有合适的时机,我们就提供代码样本(既有暴露的代码也有无暴露的代码)。
对付高严重性暴露的技巧
限制对变量的访问
不要依赖包作用域
使类不可序列化
避免硬编码敏感数据
限制对变量的访问
影响
清单 1 演示了带有 public 变量的代码,因为变量为 public 的,所以它暴露了。
清单 1. 带有 public 变量的代码
public int id;
Test(){
name = "hello world";
//code
public class MyClass extends Test{
this.name = name; // this should not be allowed
public static void main(String args){
obj.id = 123; // this should not be allowed
mc.methodIllegalSet("Illegal Set Value");
}
建议
清单 2. 不带有 public 变量的代码
private int id;
Test(){
name = "hello world";
public void setId(int id){
}
this.name = name;
public int getId(){
}
return name;
}
让每个类和方法都为 final
影响
建议
不要依赖包作用域
影响
建议
使类不可克隆
影响
class MyClass{
private int id;
public MyClass(){
name="HaryPorter";
public MyClass(int id,String name){
this.name=name;
public void display(){
"+"Name="+name);
}
public class Hacker extends MyClass implements Cloneable {
public static void main(String args){
try{
o.display();
catch(CloneNotSupportedException e){
}
}
建议
清单 4. 使您的代码不可克隆
throws java.lang.CloneNotSupportedException{
throw new java.lang.CloneNotSupportedException();
如果想让您的类可克隆并且您已经考虑了这一选择的后果,那么您仍然可以保护您的类。要做到这一点,请在您的类中定义一个为 final 的克隆方法,并让它依赖于您的一个超类中的一个非 final 克隆方法,如清单 5 中所示
清单 5. 以安全的方式使您的代码可克隆
throws java.lang.CloneNotSupportedException {
super.clone();
类中出现 clone() 方法防止攻击者重新定义您的 clone 方法。
使类不可序列化
影响
建议
清单 6. 防止对象序列化
throws java.io.NotSerializableException {
throw new java.io.NotSerializableException("This object cannot
}
通过将 writeObject() 方法声明为 final,防止了攻击者覆盖该方法。
使类不可逆序列化
影响
建议
清单 7. 防止对象逆序列化
throws java.io.NotSerializableException {
throw new java.io.NotSerializableException("This object cannot
}
通过将该方法声明为 final,防止了攻击者覆盖该方法。
避免硬编码敏感数据
影响
建议
这一问题的一种可能解决方案是:将敏感数据保存在属性文件中,无论什么时候需要这些数据,都可以从该文件读取。如果数据极其敏感,那么在访问属性文件时,您的应用程序应该使用一些加密/解密技术。||| 查找恶意代码
这样的恶意代码有三类
类中的 main 方法
注释中的死代码
入口点程序可能很危险而且有恶意。通常,Java 开发人员往往在其类中编写 main() 方法,这有助于测试单个类的功能。当类从测试转移到生产环境时,带有 main() 方法的类就成为了对应用程序的潜在威胁,因为闯入者将它们用作入口点。
请检查代码中是否有未使用的方法出现。这些方法在测试期间将会通过所有的安全检查,因为在代码中不调用它们 ? 但它们可能含有硬编码在它们内部的敏感数据(虽然是测试数据)。引入一小段代码的攻击者随后可能调用这样的方法。
避免最终应用程序中的死代码(注释内的代码)。如果闯入者去掉了对这样的代码的注释,那么代码可能会影响系统的功能性。
可以在清单 8 中看到所有三种类型的恶意代码的示例
清单 8. 潜在恶意的 Java 代码
// code written to harm the system
public void usedMethod(){
//might affect the system if uncommented
// x=x+10; //Code in comment, might affect the
}
建议
对付中等严重性暴露的技巧
不要依赖初始化
不要使用内部类
您可以不运行构造器而分配对象。这些对象使用起来不安全,因为它们不是通过构造器初始化的。
影响
例如,请想象为客户创建新帐户的 Account 对象。只有在 Account 期初余额大于 0 时,才可以开设新帐户。可以在构造器里执行这样的验证。有些人未执行构造器而创建 Account 对象,他可能创建了一个具有一些负值的新帐户,这样会使系统不一致,容易受到进一步的干预。
建议
清单 9. 使用布尔标志以检查初始化过程
private boolean initialized = false;
public MyClass (){
method1();
}
private void method1(){ //no need to check for initialization variable
}
public void method2(){
if(initialized==true){
}
else{
}
e.printStackTrace();
}
如果对象由逆序列化进行初始化,那么上面讨论的验证机制将难以奏效,因为在该过程中并不调用构造器。在这种情况下,类应该实现 ObjectInputValidation 接口
清单 10. 实现 ObjectInputValidation
interface java.io.ObjectInputValidation {
}
所有验证都应该在 validateObject() 方法中执行。对象还必须调用 ObjectInputStream.RegisterValidation() 方法以为逆序列化对象之后的验证进行注册。 RegisterValidation() 的第一个参数是实现 validateObject() 的对象,通常是对对象自身的引用。注:任何实现 validateObject() 的对象都可能充当对象验证器,但对象通常验证它自己对其它对象的引用。RegisterValidation() 的第二个参数是一个确定回调顺序的整数优先级,优先级数字大的比优先级数字小的先回调。同一优先级内的回调顺序则不确定。
当对象已逆序列化时,ObjectInputStream 按照从高到低的优先级顺序调用每个已注册对象上的 validateObject()。
不要通过名称来比较类
影响
例如,请假设您想确定某个对象是否是类 com.bar.Foo 的实例。清单 11 演示了完成这一任务的错误方法
清单 11. 比较类的错误方法
// objects class is named Foo
// object's class has some other name
建议
清单 12. 比较类的更好方法
// object's class is equal to
}else{
// this class calls "com.bar.Foo"
然而,比较类的更好方法是直接比较类对象看它们是否相等。例如,如果您想确定两个对象 a 和 b 是否属同一个类,那么您就应该使用清单 13 中的代码:||| 清单 13. 直接比较对象来看它们是否相等
// objects have the same class
// objects have different classes
尽可能少用直接名称比较。
不要使用内部类
影响
建议 如果能够不使用内部类就不要使用内部类。
对付低严重性暴露的技巧
避免返回可变对象
避免返回可变对象
影响
清单 14 演示了脆弱性。getExposedObj() 方法返回了 Exposed 对象的引用副本,该对象是可变的
清单 14. 返回可变对象的引用副本
private int id;
public Exposed(){
public Exposed(int id, String name){
this.name = name;
public int getId(){
}
return name;
public void setId(int id){
}
this.name = name;
public void display(){
}
public class Exp12{
public Exposed getExposedObj(){
}
Exp12 exp12 = new Exp12();
Exposed exposed = exp12.getExposedObj();
exposed.setName("Hacker");
}
建议
清单 15. 返回可变对象的副本
return new Exposed(exposedObj.getId(),exposedObj.getName());
或者,您的代码也可以返回 Exposed 对象的克隆。
检查本机方法
影响
建议
它们返回什么
它们是否绕过安全性检查
它们是否含有绕过包边界从而绕过包保护的方法调用
编写安全 Java 代码是十分困难的,但描述了一些可行的实践来帮您编写安全 Java 代码。这些建议并不能解决您的所有安全性问题,但它们将减少暴露数目。最佳软件安全性实践可以帮助确保软件正常运行。安全至关重要和高可靠系统设计者总是花费大量精力来分析和跟踪软件行为。只有通过将安全性作为至关紧要的系统特性来对待 ? 并且从一开始就将它构建到应用程序中,我们才可以避免亡羊补牢似的、修修补补的安全性方法。
编辑推荐:
温馨提示:因考试政策、内容不断变化与调整,长理培训网站提供的以上信息仅供参考,如有异议,请考生以权威部门公布的内容为准! (责任编辑:长理培训)
点击加载更多评论>>