java安全编程标准笔记(一)

一、表达式规范

1. 确保使用正确的类型来自动封装数值

简介

自动封装是java中基本类型与其封装类型的相关转换的操作,但有时如果不注意会产生意想不到的情况,如下代码,我们期望的结果应该是集合的大小应该为1,然而实际并非如此.

public static void main(String[] args) {
		HashSet<Short> s = new HashSet<>();
		for (short i = 0; i < 100; i++) {
			s.add(i);
			s.remove(i-1);
		}
		System.out.println(s.size()); //打印结果为100
	}
问题说明

在操作 i-1 的时候,将shortint类型的值结合起来,它会产生自动类型转换,将其转换到一个int类型,在存到集合时,转成Integer类型,而不是转成short类型,因为这个HashSet集合只有Short类型,所以这个移除Integer类型的动作就没有发生。

修正办法
public static void main(String[] args) {
		HashSet<Short> s = new HashSet<>();
		for (short i = 0; i < 100; i++) {
			s.add(i);
			s.remove((short)(i-1));
		}
		System.out.println(s.size()); 打印结果为1
	}

2、确保构造函数不会调用可覆写的方法

简介

在创建对象时用可覆写的方法可能造成使用未初始化的数据,进而导致执行异常或者预期外的结果。在构造函数中调用可覆写的方法也会在对象创建完成前泄露this引用,这使得其他线程可能访问到未初始化或不一致的数据。

class SuperClass{
    public SuperClass() {
        doLogic();
    }

    public void doLogic() {
        System.out.println("this is super class");
    }
}
class SubClass extends SuperClass{
    private String color = null;
    SubClass(){
        super();
        color = "red";
    }
    public void doLogic() {
        System.out.println("this is sub class,color: " + color);
    }
}
public class Main {    
    public static void main(String[] args) {
        SuperClass superClass = new SuperClass(); //this is super class
        SubClass subClass = new SubClass();//this is sub class,color: null
        subClass.doLogic();//this is sub class,color: red
        superClass.doLogic();//this is super class

    }
}
问题说明

方法doLogic()是在基类的构造函数中调用的。当直接创建基类的实例时,将调用基类中的方法doLogic(),从而正确的完成执行。然而,当子类发起基类的创建时,却调用子类的doLogic()方法。这种情况下,因为子类的构造函数还有完成执行,所以color的值为null

修正办法

符合规则的方案是把基类的doLogic()声明为private,从而确保此方法不会被覆写。总结为构造函数只能调用final或者private修饰的方法。

二、方法规范

1.确保比较等同的对象能得到相等的结果

简介

一个对象是同时以它的标识(内存中的位置)和它的状态(实际的数据)作为特征的。操作符 ==只是比较两个对象的标识;java.lang.Object默认的比较是实现比较对象标识,如果一个类定义了equals()方法,则意味着覆写了java.lang.Object中定义的方法,会用来比较对象的状态。当一个类中没有自定义的equals()方法时,在比较时会调用Object类中的方法。

java语言规范指定的equals()的通常合约用法有5点要求:
1)方法是自反的:对于任何引用x,x.equals(x)必须返回true
2)方法是对称的:对于任何引用xyx.equals(y)必须返回true,当且仅当y.equals(x)返回true
3)方法是可传递的:对于任何引用xyz,如果x.equals(y)返回true并且y.equals(z)返回true,则x.equals(z)返回true
4)方法是一致的:只要用在equals()中比较的对象信息没有改变,对于任何引用xy,多次调用x.equals(y)总是返回true或者总是返回false
5)对于任何非空的引用,x.equals(null)总是返回false。

错误示例一

以下代码违反了对称性

public class Sub {
	private String s;
	
	public Sub(String s) {
		if(s == null) {
			throw new NullPointerException();
		}
		this.s = s;
	}

	public boolean equals(Object o) {
		if(o instanceof Sub) {
			return s.equalsIgnoreCase(((Sub) o).s);
		}
		if(o instanceof String) {
			return s.equalsIgnoreCase((String)o);
		}
		return false;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Sub sub = new Sub("Java");
		String s = "java";
		System.out.println(sub.equals(s)); //输出true
		System.out.println(s.equals(sub)); //输出false
	}
}
问题说明

覆写的equals()方法违反了第二条合约要求(对称)。

修正办法

覆写的equals()方法只允许传入该类的对象,保证比较的对象实例是同一个对象

public class Sub {
	private String s;
	
	public Sub(String s) {
		if(s == null) {
			throw new NullPointerException();
		}
		this.s = s;
	}

	public boolean equals(Object o) {
		return (o instanceof Sub) && ((Sub)o.s.equalsIgnoreCase(s));
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Sub sub = new Sub("Java");
		String s = "java";
		System.out.println(sub.equals(s)); //输出false
		System.out.println(s.equals(sub)); //输出false
	}
}
错误示例二

统一资源定位器(URL)指定了资源的地址和访问资源的方法。根据Java API中URL类的文档描述:
如果两个URL对象有相同的协议、引用、等价的主机、主机端口、相同的文件及文件片段,则这两个URL是相等的。
在以下的情况中,认为两个主机是等同的:当能够将两个主机的名字解析成同样的IP地址时(DNS解析);或者当其中一个主机名字不能解析时,两个主机名在不考虑大小写时是相同的;或者两个主机名都等于null
1)对于HTTP的虚拟机来说,为equals()方法定义的行为是不一致的。虚拟主机允许一个网络服务器可以在一台电脑上处理多个网站,并有时会共享相同的IP地址。遗憾的是,在设计URL类的时候没有考虑到这种技术。因此,当两个不同的URL被解析成相同的IP地址时,URL类认为它们是相等的。
2)另一个对URL对象使用equals()方法带来的风险是该方法在有网络连接和没有网络连接时,使用的逻辑是不同的。当连接到网络时,该方法遵循java API中描述的步骤;当没有网络连接时,该方法对两个URL进行字符串进行比较。从而,URL.equals()方法违反了一致性约定的要求。

public class Filter{
	public static main(String[] args) throws MalformedURLException {
		final URL allow = new URL("http://mailwebsite.com");
		if (!allow.equals(new URL(args[0]))) {
			throw new SecurityException("Access Denied");
		}
	}
}
修正办法
方法一

这个解决方案还是存在问题。两个有着不同字符串表达的URL还是可能指向同一个资源的。然而,因为维持了equals()的合约,所以这个方案在这种情况下会安全地终止任务,并且系统不允许错误地接受一个恶意的URL

public class Filter{
	public static main(String[] args) throws MalformedURLException {
		final URL allow = new URL("http://mailwebsite.com");
		if (!allow.toString().equals(new URL(args[0]).toString())) {
			throw new SecurityException("Access Denied");
		}
	}
}
方法二

统一资源定位符(URI)包含一个字符串来定位资源;这是一个比URL范围更广的概念。Java.net.URL类提供了基于字符串的equals()hashCode()方法来满足基类方法大体上的合约;它们不会调用主机名解析而且不会受网络连接的影响。并且,URL.toURI()URI.toURL()方法方便的在这个两个类之间进行转换。程序应尽可能的使用URI

public class Filter{
	public static main(String[] args) throws MalformedURLException,URISyntaxException {
		final URI allow = new URI("http://mailwebsite.com");
		if (!allow.toString().equals(new URI(args[0]).toString())) {
			throw new SecurityException("Access Denied");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值